@jsonstudio/rcc 0.89.168 → 0.89.524

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (431) hide show
  1. package/README.md +18 -15
  2. package/dist/build-info.js +3 -3
  3. package/dist/build-info.js.map +1 -1
  4. package/dist/cli.js +94 -0
  5. package/dist/cli.js.map +1 -1
  6. package/dist/client/gemini-cli/gemini-cli-protocol-client.js +28 -5
  7. package/dist/client/gemini-cli/gemini-cli-protocol-client.js.map +1 -1
  8. package/dist/commands/token-daemon.d.ts +2 -0
  9. package/dist/commands/token-daemon.js +183 -0
  10. package/dist/commands/token-daemon.js.map +1 -0
  11. package/dist/error-handling/quiet-error-handling-center.d.ts +9 -0
  12. package/dist/error-handling/quiet-error-handling-center.js +141 -0
  13. package/dist/error-handling/quiet-error-handling-center.js.map +1 -0
  14. package/dist/error-handling/route-error-hub.js +8 -2
  15. package/dist/error-handling/route-error-hub.js.map +1 -1
  16. package/dist/index.js +4 -3
  17. package/dist/index.js.map +1 -1
  18. package/dist/modules/llmswitch/bridge.d.ts +1 -1
  19. package/dist/modules/llmswitch/bridge.js +3 -2
  20. package/dist/modules/llmswitch/bridge.js.map +1 -1
  21. package/dist/modules/pipeline/utils/colored-logger.d.ts +2 -0
  22. package/dist/modules/pipeline/utils/colored-logger.js +22 -3
  23. package/dist/modules/pipeline/utils/colored-logger.js.map +1 -1
  24. package/dist/providers/auth/antigravity-userinfo-helper.d.ts +10 -0
  25. package/dist/providers/auth/antigravity-userinfo-helper.js +140 -0
  26. package/dist/providers/auth/antigravity-userinfo-helper.js.map +1 -0
  27. package/dist/providers/auth/gemini-cli-userinfo-helper.js +12 -2
  28. package/dist/providers/auth/gemini-cli-userinfo-helper.js.map +1 -1
  29. package/dist/providers/auth/oauth-lifecycle.js +395 -24
  30. package/dist/providers/auth/oauth-lifecycle.js.map +1 -1
  31. package/dist/providers/auth/token-scanner/index.d.ts +32 -0
  32. package/dist/providers/auth/token-scanner/index.js +86 -0
  33. package/dist/providers/auth/token-scanner/index.js.map +1 -0
  34. package/dist/providers/auth/tokenfile-auth.d.ts +17 -0
  35. package/dist/providers/auth/tokenfile-auth.js +27 -5
  36. package/dist/providers/auth/tokenfile-auth.js.map +1 -1
  37. package/dist/providers/core/api/provider-types.d.ts +10 -0
  38. package/dist/providers/core/config/oauth-flows.d.ts +25 -0
  39. package/dist/providers/core/config/oauth-flows.js +92 -5
  40. package/dist/providers/core/config/oauth-flows.js.map +1 -1
  41. package/dist/providers/core/config/provider-oauth-configs.js +93 -2
  42. package/dist/providers/core/config/provider-oauth-configs.js.map +1 -1
  43. package/dist/providers/core/config/service-profiles.js +18 -10
  44. package/dist/providers/core/config/service-profiles.js.map +1 -1
  45. package/dist/providers/core/runtime/base-provider.d.ts +2 -0
  46. package/dist/providers/core/runtime/base-provider.js +135 -15
  47. package/dist/providers/core/runtime/base-provider.js.map +1 -1
  48. package/dist/providers/core/runtime/gemini-cli-http-provider.d.ts +8 -3
  49. package/dist/providers/core/runtime/gemini-cli-http-provider.js +332 -67
  50. package/dist/providers/core/runtime/gemini-cli-http-provider.js.map +1 -1
  51. package/dist/providers/core/runtime/http-request-executor.d.ts +1 -0
  52. package/dist/providers/core/runtime/http-request-executor.js +41 -1
  53. package/dist/providers/core/runtime/http-request-executor.js.map +1 -1
  54. package/dist/providers/core/runtime/http-transport-provider.d.ts +27 -0
  55. package/dist/providers/core/runtime/http-transport-provider.js +342 -69
  56. package/dist/providers/core/runtime/http-transport-provider.js.map +1 -1
  57. package/dist/providers/core/runtime/provider-error-classifier.d.ts +2 -2
  58. package/dist/providers/core/runtime/provider-error-classifier.js +14 -4
  59. package/dist/providers/core/runtime/provider-error-classifier.js.map +1 -1
  60. package/dist/providers/core/runtime/provider-factory.d.ts +1 -0
  61. package/dist/providers/core/runtime/provider-factory.js +37 -8
  62. package/dist/providers/core/runtime/provider-factory.js.map +1 -1
  63. package/dist/providers/core/runtime/responses-provider.d.ts +3 -3
  64. package/dist/providers/core/runtime/responses-provider.js +56 -117
  65. package/dist/providers/core/runtime/responses-provider.js.map +1 -1
  66. package/dist/providers/core/runtime/vision-debug-utils.d.ts +13 -0
  67. package/dist/providers/core/runtime/vision-debug-utils.js +114 -0
  68. package/dist/providers/core/runtime/vision-debug-utils.js.map +1 -0
  69. package/dist/providers/core/strategies/oauth-auth-code-flow.js +82 -25
  70. package/dist/providers/core/strategies/oauth-auth-code-flow.js.map +1 -1
  71. package/dist/providers/core/utils/http-client.d.ts +5 -0
  72. package/dist/providers/core/utils/http-client.js +31 -4
  73. package/dist/providers/core/utils/http-client.js.map +1 -1
  74. package/dist/providers/core/utils/provider-error-reporter.js +8 -2
  75. package/dist/providers/core/utils/provider-error-reporter.js.map +1 -1
  76. package/dist/providers/core/utils/snapshot-writer.d.ts +1 -1
  77. package/dist/providers/core/utils/snapshot-writer.js +5 -1
  78. package/dist/providers/core/utils/snapshot-writer.js.map +1 -1
  79. package/dist/providers/profile/provider-profile-loader.js +8 -4
  80. package/dist/providers/profile/provider-profile-loader.js.map +1 -1
  81. package/dist/runtime/runtime-flags.d.ts +4 -0
  82. package/dist/runtime/runtime-flags.js +32 -0
  83. package/dist/runtime/runtime-flags.js.map +1 -0
  84. package/dist/server/handlers/handler-utils.js +29 -2
  85. package/dist/server/handlers/handler-utils.js.map +1 -1
  86. package/dist/server/handlers/messages-handler.js +27 -26
  87. package/dist/server/handlers/messages-handler.js.map +1 -1
  88. package/dist/server/handlers/responses-handler.js +35 -1
  89. package/dist/server/handlers/responses-handler.js.map +1 -1
  90. package/dist/server/handlers/sse-dispatcher.js +22 -2
  91. package/dist/server/handlers/sse-dispatcher.js.map +1 -1
  92. package/dist/server/runtime/http-server/index.d.ts +10 -0
  93. package/dist/server/runtime/http-server/index.js +551 -148
  94. package/dist/server/runtime/http-server/index.js.map +1 -1
  95. package/dist/server/runtime/http-server/request-executor.d.ts +14 -0
  96. package/dist/server/runtime/http-server/request-executor.js +638 -149
  97. package/dist/server/runtime/http-server/request-executor.js.map +1 -1
  98. package/dist/server/runtime/http-server/routes.d.ts +5 -0
  99. package/dist/server/runtime/http-server/routes.js +69 -0
  100. package/dist/server/runtime/http-server/routes.js.map +1 -1
  101. package/dist/server/runtime/http-server/runtime-manager.js +18 -0
  102. package/dist/server/runtime/http-server/runtime-manager.js.map +1 -1
  103. package/dist/server/utils/sse-request-parser.d.ts +1 -0
  104. package/dist/server/utils/sse-request-parser.js +17 -6
  105. package/dist/server/utils/sse-request-parser.js.map +1 -1
  106. package/dist/server/utils/utf8-chunk-buffer.d.ts +43 -0
  107. package/dist/server/utils/utf8-chunk-buffer.js +132 -0
  108. package/dist/server/utils/utf8-chunk-buffer.js.map +1 -0
  109. package/dist/server/utils/warmup-detector.d.ts +7 -0
  110. package/dist/server/utils/warmup-detector.js +125 -0
  111. package/dist/server/utils/warmup-detector.js.map +1 -0
  112. package/dist/server/utils/warmup-storm-tracker.d.ts +9 -0
  113. package/dist/server/utils/warmup-storm-tracker.js +61 -0
  114. package/dist/server/utils/warmup-storm-tracker.js.map +1 -0
  115. package/dist/token-daemon/index.d.ts +7 -0
  116. package/dist/token-daemon/index.js +242 -0
  117. package/dist/token-daemon/index.js.map +1 -0
  118. package/dist/token-daemon/server-utils.d.ts +33 -0
  119. package/dist/token-daemon/server-utils.js +155 -0
  120. package/dist/token-daemon/server-utils.js.map +1 -0
  121. package/dist/token-daemon/token-daemon.d.ts +20 -0
  122. package/dist/token-daemon/token-daemon.js +144 -0
  123. package/dist/token-daemon/token-daemon.js.map +1 -0
  124. package/dist/token-daemon/token-types.d.ts +44 -0
  125. package/dist/token-daemon/token-types.js +18 -0
  126. package/dist/token-daemon/token-types.js.map +1 -0
  127. package/dist/token-daemon/token-utils.d.ts +17 -0
  128. package/dist/token-daemon/token-utils.js +153 -0
  129. package/dist/token-daemon/token-utils.js.map +1 -0
  130. package/dist/tools/semantic-replay.js +7 -6
  131. package/dist/tools/semantic-replay.js.map +1 -1
  132. package/dist/utils/debug-utils.js +14 -0
  133. package/dist/utils/debug-utils.js.map +1 -1
  134. package/dist/utils/error-handler-registry.d.ts +36 -0
  135. package/dist/utils/error-handler-registry.js +99 -12
  136. package/dist/utils/error-handler-registry.js.map +1 -1
  137. package/dist/utils/error-handling-utils.js +4 -3
  138. package/dist/utils/error-handling-utils.js.map +1 -1
  139. package/dist/utils/log-helpers.d.ts +6 -0
  140. package/dist/utils/log-helpers.js +90 -0
  141. package/dist/utils/log-helpers.js.map +1 -0
  142. package/dist/utils/logger.d.ts +8 -0
  143. package/dist/utils/logger.js +55 -2
  144. package/dist/utils/logger.js.map +1 -1
  145. package/dist/utils/snapshot-writer.js +2 -6
  146. package/dist/utils/snapshot-writer.js.map +1 -1
  147. package/node_modules/@jsonstudio/llms/README.md +2 -0
  148. package/node_modules/@jsonstudio/llms/dist/conversion/codecs/gemini-openai-codec.js +152 -6
  149. package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/auto-thinking.d.ts +6 -0
  150. package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/auto-thinking.js +25 -0
  151. package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/field-mapping.d.ts +14 -0
  152. package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/field-mapping.js +155 -0
  153. package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/gemini-web-search.d.ts +17 -0
  154. package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/gemini-web-search.js +68 -0
  155. package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/glm-image-content.d.ts +2 -0
  156. package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/glm-image-content.js +83 -0
  157. package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/glm-tool-extraction.d.ts +2 -0
  158. package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/glm-tool-extraction.js +264 -0
  159. package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/glm-vision-prompt.d.ts +11 -0
  160. package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/glm-vision-prompt.js +177 -0
  161. package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/glm-web-search.d.ts +2 -0
  162. package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/glm-web-search.js +63 -0
  163. package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/qwen-transform.d.ts +3 -0
  164. package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/qwen-transform.js +209 -0
  165. package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/request-rules.d.ts +24 -0
  166. package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/request-rules.js +63 -0
  167. package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/response-blacklist.d.ts +14 -0
  168. package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/response-blacklist.js +85 -0
  169. package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/response-normalize.d.ts +5 -0
  170. package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/response-normalize.js +121 -0
  171. package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/response-validate.d.ts +5 -0
  172. package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/response-validate.js +76 -0
  173. package/{dist/providers/compat/utils/snapshot-writer.d.ts → node_modules/@jsonstudio/llms/dist/conversion/compat/actions/snapshot.d.ts} +2 -2
  174. package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/snapshot.js +21 -0
  175. package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/tool-schema.d.ts +6 -0
  176. package/{dist/providers/compat/glm/utils/tool-schema-helpers.js → node_modules/@jsonstudio/llms/dist/conversion/compat/actions/tool-schema.js} +6 -1
  177. package/{dist/providers/compat/filters → node_modules/@jsonstudio/llms/dist/conversion/compat/actions}/universal-shape-filter.d.ts +17 -22
  178. package/{dist/providers/compat/filters → node_modules/@jsonstudio/llms/dist/conversion/compat/actions}/universal-shape-filter.js +46 -99
  179. package/node_modules/@jsonstudio/llms/dist/conversion/compat/profiles/chat-gemini.json +17 -0
  180. package/node_modules/@jsonstudio/llms/dist/conversion/compat/profiles/chat-glm.json +196 -13
  181. package/node_modules/@jsonstudio/llms/dist/conversion/compat/profiles/chat-iflow.json +194 -26
  182. package/node_modules/@jsonstudio/llms/dist/conversion/compat/profiles/chat-lmstudio.json +43 -35
  183. package/node_modules/@jsonstudio/llms/dist/conversion/compat/profiles/chat-qwen.json +20 -16
  184. package/node_modules/@jsonstudio/llms/dist/conversion/compat/profiles/responses-c4m.json +42 -42
  185. package/node_modules/@jsonstudio/llms/dist/conversion/config/sample-config.json +1 -1
  186. package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/compat/compat-engine.d.ts +7 -2
  187. package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/compat/compat-engine.js +5 -665
  188. package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/compat/compat-pipeline-executor.d.ts +9 -0
  189. package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/compat/compat-pipeline-executor.js +869 -0
  190. package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/compat/compat-types.d.ts +55 -0
  191. package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/hub-pipeline.d.ts +2 -0
  192. package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/hub-pipeline.js +74 -5
  193. package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage3_compat/index.js +2 -2
  194. package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/target-utils.js +9 -0
  195. package/node_modules/@jsonstudio/llms/dist/conversion/hub/process/chat-process.js +213 -1
  196. package/node_modules/@jsonstudio/llms/dist/conversion/hub/response/provider-response.d.ts +34 -0
  197. package/node_modules/@jsonstudio/llms/dist/conversion/hub/response/provider-response.js +84 -24
  198. package/node_modules/@jsonstudio/llms/dist/conversion/hub/response/response-runtime.js +19 -2
  199. package/node_modules/@jsonstudio/llms/dist/conversion/hub/response/server-side-tools.d.ts +26 -0
  200. package/node_modules/@jsonstudio/llms/dist/conversion/hub/response/server-side-tools.js +383 -0
  201. package/node_modules/@jsonstudio/llms/dist/conversion/hub/semantic-mappers/gemini-mapper.js +241 -14
  202. package/node_modules/@jsonstudio/llms/dist/conversion/hub/semantic-mappers/responses-mapper.js +17 -1
  203. package/node_modules/@jsonstudio/llms/dist/conversion/hub/standardized-bridge.js +14 -0
  204. package/node_modules/@jsonstudio/llms/dist/conversion/hub/types/standardized.d.ts +1 -0
  205. package/node_modules/@jsonstudio/llms/dist/conversion/responses/responses-host-policy.d.ts +6 -0
  206. package/node_modules/@jsonstudio/llms/dist/conversion/responses/responses-host-policy.js +14 -0
  207. package/node_modules/@jsonstudio/llms/dist/conversion/responses/responses-openai-bridge.js +133 -5
  208. package/node_modules/@jsonstudio/llms/dist/conversion/shared/anthropic-message-utils.js +98 -3
  209. package/node_modules/@jsonstudio/llms/dist/conversion/shared/bridge-message-utils.js +137 -10
  210. package/node_modules/@jsonstudio/llms/dist/conversion/shared/responses-output-builder.js +43 -2
  211. package/node_modules/@jsonstudio/llms/dist/conversion/shared/responses-reasoning-registry.d.ts +4 -0
  212. package/node_modules/@jsonstudio/llms/dist/conversion/shared/responses-reasoning-registry.js +62 -1
  213. package/node_modules/@jsonstudio/llms/dist/conversion/shared/responses-response-utils.js +23 -1
  214. package/node_modules/@jsonstudio/llms/dist/conversion/shared/snapshot-utils.js +17 -47
  215. package/node_modules/@jsonstudio/llms/dist/conversion/shared/tool-canonicalizer.d.ts +2 -0
  216. package/node_modules/@jsonstudio/llms/dist/conversion/shared/tool-filter-pipeline.js +12 -0
  217. package/node_modules/@jsonstudio/llms/dist/conversion/shared/tool-mapping.js +25 -2
  218. package/node_modules/@jsonstudio/llms/dist/index.d.ts +1 -0
  219. package/node_modules/@jsonstudio/llms/dist/index.js +1 -0
  220. package/node_modules/@jsonstudio/llms/dist/router/virtual-router/bootstrap.js +540 -36
  221. package/node_modules/@jsonstudio/llms/dist/router/virtual-router/classifier.js +12 -11
  222. package/node_modules/@jsonstudio/llms/dist/router/virtual-router/context-advisor.d.ts +19 -0
  223. package/node_modules/@jsonstudio/llms/dist/router/virtual-router/context-advisor.js +64 -0
  224. package/node_modules/@jsonstudio/llms/dist/router/virtual-router/engine.d.ts +26 -0
  225. package/node_modules/@jsonstudio/llms/dist/router/virtual-router/engine.js +450 -54
  226. package/node_modules/@jsonstudio/llms/dist/router/virtual-router/features.js +23 -458
  227. package/node_modules/@jsonstudio/llms/dist/router/virtual-router/health-manager.js +2 -7
  228. package/node_modules/@jsonstudio/llms/dist/router/virtual-router/message-utils.d.ts +7 -0
  229. package/node_modules/@jsonstudio/llms/dist/router/virtual-router/message-utils.js +78 -0
  230. package/node_modules/@jsonstudio/llms/dist/router/virtual-router/provider-registry.js +7 -2
  231. package/node_modules/@jsonstudio/llms/dist/router/virtual-router/token-counter.js +14 -3
  232. package/node_modules/@jsonstudio/llms/dist/router/virtual-router/token-estimator.d.ts +2 -0
  233. package/node_modules/@jsonstudio/llms/dist/router/virtual-router/token-estimator.js +16 -0
  234. package/node_modules/@jsonstudio/llms/dist/router/virtual-router/token-file-scanner.d.ts +15 -0
  235. package/node_modules/@jsonstudio/llms/dist/router/virtual-router/token-file-scanner.js +56 -0
  236. package/node_modules/@jsonstudio/llms/dist/router/virtual-router/tool-signals.d.ts +13 -0
  237. package/node_modules/@jsonstudio/llms/dist/router/virtual-router/tool-signals.js +403 -0
  238. package/node_modules/@jsonstudio/llms/dist/router/virtual-router/types.d.ts +86 -2
  239. package/node_modules/@jsonstudio/llms/dist/router/virtual-router/types.js +3 -1
  240. package/node_modules/@jsonstudio/llms/dist/servertool/engine.d.ts +27 -0
  241. package/node_modules/@jsonstudio/llms/dist/servertool/engine.js +60 -0
  242. package/node_modules/@jsonstudio/llms/dist/servertool/flow-types.d.ts +40 -0
  243. package/node_modules/@jsonstudio/llms/dist/servertool/flow-types.js +1 -0
  244. package/node_modules/@jsonstudio/llms/dist/servertool/handlers/vision.d.ts +1 -0
  245. package/node_modules/@jsonstudio/llms/dist/servertool/handlers/vision.js +194 -0
  246. package/node_modules/@jsonstudio/llms/dist/servertool/handlers/web-search.d.ts +1 -0
  247. package/node_modules/@jsonstudio/llms/dist/servertool/handlers/web-search.js +638 -0
  248. package/node_modules/@jsonstudio/llms/dist/servertool/orchestration-types.d.ts +33 -0
  249. package/node_modules/@jsonstudio/llms/dist/servertool/orchestration-types.js +1 -0
  250. package/node_modules/@jsonstudio/llms/dist/servertool/registry.d.ts +18 -0
  251. package/node_modules/@jsonstudio/llms/dist/servertool/registry.js +27 -0
  252. package/node_modules/@jsonstudio/llms/dist/servertool/server-side-tools.d.ts +8 -0
  253. package/node_modules/@jsonstudio/llms/dist/servertool/server-side-tools.js +208 -0
  254. package/node_modules/@jsonstudio/llms/dist/servertool/types.d.ts +88 -0
  255. package/node_modules/@jsonstudio/llms/dist/servertool/types.js +1 -0
  256. package/node_modules/@jsonstudio/llms/dist/servertool/vision-tool.d.ts +2 -0
  257. package/node_modules/@jsonstudio/llms/dist/servertool/vision-tool.js +185 -0
  258. package/node_modules/@jsonstudio/llms/dist/sse/json-to-sse/event-generators/responses.js +15 -3
  259. package/node_modules/@jsonstudio/llms/dist/sse/sse-to-json/builders/response-builder.js +6 -3
  260. package/node_modules/@jsonstudio/llms/dist/sse/sse-to-json/gemini-sse-to-json-converter.js +27 -1
  261. package/node_modules/@jsonstudio/llms/dist/sse/types/gemini-types.d.ts +20 -1
  262. package/node_modules/@jsonstudio/llms/dist/sse/types/responses-types.js +1 -1
  263. package/node_modules/@jsonstudio/llms/dist/telemetry/stats-center.d.ts +73 -0
  264. package/node_modules/@jsonstudio/llms/dist/telemetry/stats-center.js +280 -0
  265. package/node_modules/@jsonstudio/llms/package.json +2 -2
  266. package/package.json +11 -10
  267. package/scripts/README.md +26 -12
  268. package/scripts/auth-antigravity-token.mjs +64 -0
  269. package/scripts/auth-gemini-cli-token.mjs +96 -0
  270. package/scripts/auth-iflow-manual.mjs +81 -0
  271. package/scripts/auth-iflow-token-direct.mjs +87 -0
  272. package/scripts/auth-iflow-token.mjs +77 -0
  273. package/scripts/copy-compat-assets.mjs +3 -15
  274. package/scripts/install-verify.mjs +1 -0
  275. package/scripts/pack-mode.mjs +2 -1
  276. package/scripts/publish-rcc.mjs +20 -4
  277. package/scripts/replay-codex-sample.mjs +13 -8
  278. package/scripts/tests/chat-pipeline-blackbox.mjs +1 -1
  279. package/scripts/tests/virtual-router-health.mjs +141 -6
  280. package/scripts/tools/capture-provider-goldens.mjs +8 -7
  281. package/scripts/verify-client-headers.mjs +224 -0
  282. package/dist/providers/compat/base-compatibility.d.ts +0 -27
  283. package/dist/providers/compat/base-compatibility.js +0 -143
  284. package/dist/providers/compat/base-compatibility.js.map +0 -1
  285. package/dist/providers/compat/compat-directory-loader.d.ts +0 -4
  286. package/dist/providers/compat/compat-directory-loader.js +0 -85
  287. package/dist/providers/compat/compat-directory-loader.js.map +0 -1
  288. package/dist/providers/compat/compatibility-adapter.d.ts +0 -18
  289. package/dist/providers/compat/compatibility-adapter.js +0 -104
  290. package/dist/providers/compat/compatibility-adapter.js.map +0 -1
  291. package/dist/providers/compat/compatibility-factory.d.ts +0 -57
  292. package/dist/providers/compat/compatibility-factory.js +0 -155
  293. package/dist/providers/compat/compatibility-factory.js.map +0 -1
  294. package/dist/providers/compat/compatibility-interface.d.ts +0 -35
  295. package/dist/providers/compat/compatibility-interface.js +0 -2
  296. package/dist/providers/compat/compatibility-interface.js.map +0 -1
  297. package/dist/providers/compat/compatibility-manager.d.ts +0 -85
  298. package/dist/providers/compat/compatibility-manager.js +0 -368
  299. package/dist/providers/compat/compatibility-manager.js.map +0 -1
  300. package/dist/providers/compat/config/config-compatibility.d.ts +0 -28
  301. package/dist/providers/compat/config/config-compatibility.js +0 -95
  302. package/dist/providers/compat/config/config-compatibility.js.map +0 -1
  303. package/dist/providers/compat/field-mapping.d.ts +0 -102
  304. package/dist/providers/compat/field-mapping.js +0 -447
  305. package/dist/providers/compat/field-mapping.js.map +0 -1
  306. package/dist/providers/compat/filters/blacklist-sanitizer.d.ts +0 -45
  307. package/dist/providers/compat/filters/blacklist-sanitizer.js +0 -133
  308. package/dist/providers/compat/filters/blacklist-sanitizer.js.map +0 -1
  309. package/dist/providers/compat/filters/response-blacklist-sanitizer.d.ts +0 -28
  310. package/dist/providers/compat/filters/response-blacklist-sanitizer.js +0 -138
  311. package/dist/providers/compat/filters/response-blacklist-sanitizer.js.map +0 -1
  312. package/dist/providers/compat/filters/universal-shape-filter.js.map +0 -1
  313. package/dist/providers/compat/glm/config/blacklist-rules.json +0 -22
  314. package/dist/providers/compat/glm/config/field-mappings.json +0 -92
  315. package/dist/providers/compat/glm/config/response-blacklist.json +0 -7
  316. package/dist/providers/compat/glm/config/shape-filters.json +0 -37
  317. package/dist/providers/compat/glm/field-mapping/field-mapping-processor.d.ts +0 -28
  318. package/dist/providers/compat/glm/field-mapping/field-mapping-processor.js +0 -306
  319. package/dist/providers/compat/glm/field-mapping/field-mapping-processor.js.map +0 -1
  320. package/dist/providers/compat/glm/functions/glm-processor.d.ts +0 -50
  321. package/dist/providers/compat/glm/functions/glm-processor.js +0 -134
  322. package/dist/providers/compat/glm/functions/glm-processor.js.map +0 -1
  323. package/dist/providers/compat/glm/glm-compatibility.d.ts +0 -34
  324. package/dist/providers/compat/glm/glm-compatibility.js +0 -117
  325. package/dist/providers/compat/glm/glm-compatibility.js.map +0 -1
  326. package/dist/providers/compat/glm/hooks/base-hook.d.ts +0 -21
  327. package/dist/providers/compat/glm/hooks/base-hook.js +0 -53
  328. package/dist/providers/compat/glm/hooks/base-hook.js.map +0 -1
  329. package/dist/providers/compat/glm/hooks/glm-request-validation-hook.d.ts +0 -24
  330. package/dist/providers/compat/glm/hooks/glm-request-validation-hook.js +0 -268
  331. package/dist/providers/compat/glm/hooks/glm-request-validation-hook.js.map +0 -1
  332. package/dist/providers/compat/glm/hooks/glm-response-normalization-hook.d.ts +0 -21
  333. package/dist/providers/compat/glm/hooks/glm-response-normalization-hook.js +0 -171
  334. package/dist/providers/compat/glm/hooks/glm-response-normalization-hook.js.map +0 -1
  335. package/dist/providers/compat/glm/hooks/glm-response-validation-hook.d.ts +0 -25
  336. package/dist/providers/compat/glm/hooks/glm-response-validation-hook.js +0 -236
  337. package/dist/providers/compat/glm/hooks/glm-response-validation-hook.js.map +0 -1
  338. package/dist/providers/compat/glm/hooks/glm-tool-cleaning-hook.d.ts +0 -26
  339. package/dist/providers/compat/glm/hooks/glm-tool-cleaning-hook.js +0 -186
  340. package/dist/providers/compat/glm/hooks/glm-tool-cleaning-hook.js.map +0 -1
  341. package/dist/providers/compat/glm/index.d.ts +0 -24
  342. package/dist/providers/compat/glm/index.js +0 -29
  343. package/dist/providers/compat/glm/index.js.map +0 -1
  344. package/dist/providers/compat/glm/utils/tool-schema-helpers.d.ts +0 -3
  345. package/dist/providers/compat/glm/utils/tool-schema-helpers.js.map +0 -1
  346. package/dist/providers/compat/iflow/config/field-mappings.json +0 -92
  347. package/dist/providers/compat/iflow/config/shape-filters.json +0 -37
  348. package/dist/providers/compat/iflow/field-mapping/iflow-field-mapping-processor.d.ts +0 -34
  349. package/dist/providers/compat/iflow/field-mapping/iflow-field-mapping-processor.js +0 -386
  350. package/dist/providers/compat/iflow/field-mapping/iflow-field-mapping-processor.js.map +0 -1
  351. package/dist/providers/compat/iflow/functions/iflow-processor.d.ts +0 -53
  352. package/dist/providers/compat/iflow/functions/iflow-processor.js +0 -215
  353. package/dist/providers/compat/iflow/functions/iflow-processor.js.map +0 -1
  354. package/dist/providers/compat/iflow/hooks/base-hook.d.ts +0 -23
  355. package/dist/providers/compat/iflow/hooks/base-hook.js +0 -59
  356. package/dist/providers/compat/iflow/hooks/base-hook.js.map +0 -1
  357. package/dist/providers/compat/iflow/hooks/iflow-request-validation-hook.d.ts +0 -23
  358. package/dist/providers/compat/iflow/hooks/iflow-request-validation-hook.js +0 -279
  359. package/dist/providers/compat/iflow/hooks/iflow-request-validation-hook.js.map +0 -1
  360. package/dist/providers/compat/iflow/hooks/iflow-response-normalization-hook.d.ts +0 -20
  361. package/dist/providers/compat/iflow/hooks/iflow-response-normalization-hook.js +0 -180
  362. package/dist/providers/compat/iflow/hooks/iflow-response-normalization-hook.js.map +0 -1
  363. package/dist/providers/compat/iflow/hooks/iflow-response-validation-hook.d.ts +0 -23
  364. package/dist/providers/compat/iflow/hooks/iflow-response-validation-hook.js +0 -232
  365. package/dist/providers/compat/iflow/hooks/iflow-response-validation-hook.js.map +0 -1
  366. package/dist/providers/compat/iflow/hooks/iflow-tool-cleaning-hook.d.ts +0 -25
  367. package/dist/providers/compat/iflow/hooks/iflow-tool-cleaning-hook.js +0 -216
  368. package/dist/providers/compat/iflow/hooks/iflow-tool-cleaning-hook.js.map +0 -1
  369. package/dist/providers/compat/iflow/iflow-compatibility.d.ts +0 -24
  370. package/dist/providers/compat/iflow/iflow-compatibility.js +0 -94
  371. package/dist/providers/compat/iflow/iflow-compatibility.js.map +0 -1
  372. package/dist/providers/compat/index.d.ts +0 -59
  373. package/dist/providers/compat/index.js +0 -83
  374. package/dist/providers/compat/index.js.map +0 -1
  375. package/dist/providers/compat/lmstudio-compatibility.d.ts +0 -44
  376. package/dist/providers/compat/lmstudio-compatibility.js +0 -193
  377. package/dist/providers/compat/lmstudio-compatibility.js.map +0 -1
  378. package/dist/providers/compat/passthrough-compatibility.d.ts +0 -29
  379. package/dist/providers/compat/passthrough-compatibility.js +0 -83
  380. package/dist/providers/compat/passthrough-compatibility.js.map +0 -1
  381. package/dist/providers/compat/profiles/chat/glm/index.d.ts +0 -6
  382. package/dist/providers/compat/profiles/chat/glm/index.js +0 -6
  383. package/dist/providers/compat/profiles/chat/glm/index.js.map +0 -1
  384. package/dist/providers/compat/profiles/chat/iflow/index.d.ts +0 -6
  385. package/dist/providers/compat/profiles/chat/iflow/index.js +0 -6
  386. package/dist/providers/compat/profiles/chat/iflow/index.js.map +0 -1
  387. package/dist/providers/compat/profiles/chat/lmstudio/index.d.ts +0 -6
  388. package/dist/providers/compat/profiles/chat/lmstudio/index.js +0 -6
  389. package/dist/providers/compat/profiles/chat/lmstudio/index.js.map +0 -1
  390. package/dist/providers/compat/profiles/chat/qwen/index.d.ts +0 -6
  391. package/dist/providers/compat/profiles/chat/qwen/index.js +0 -6
  392. package/dist/providers/compat/profiles/chat/qwen/index.js.map +0 -1
  393. package/dist/providers/compat/profiles/compat/passthrough/index.d.ts +0 -6
  394. package/dist/providers/compat/profiles/compat/passthrough/index.js +0 -6
  395. package/dist/providers/compat/profiles/compat/passthrough/index.js.map +0 -1
  396. package/dist/providers/compat/profiles/responses/c4m/index.d.ts +0 -6
  397. package/dist/providers/compat/profiles/responses/c4m/index.js +0 -6
  398. package/dist/providers/compat/profiles/responses/c4m/index.js.map +0 -1
  399. package/dist/providers/compat/profiles/responses/default/index.d.ts +0 -6
  400. package/dist/providers/compat/profiles/responses/default/index.js +0 -6
  401. package/dist/providers/compat/profiles/responses/default/index.js.map +0 -1
  402. package/dist/providers/compat/profiles/responses/fai/index.d.ts +0 -6
  403. package/dist/providers/compat/profiles/responses/fai/index.js +0 -6
  404. package/dist/providers/compat/profiles/responses/fai/index.js.map +0 -1
  405. package/dist/providers/compat/profiles/responses/fc/index.d.ts +0 -6
  406. package/dist/providers/compat/profiles/responses/fc/index.js +0 -6
  407. package/dist/providers/compat/profiles/responses/fc/index.js.map +0 -1
  408. package/dist/providers/compat/qwen/index.d.ts +0 -4
  409. package/dist/providers/compat/qwen/index.js +0 -6
  410. package/dist/providers/compat/qwen/index.js.map +0 -1
  411. package/dist/providers/compat/qwen-compatibility.d.ts +0 -52
  412. package/dist/providers/compat/qwen-compatibility.js +0 -330
  413. package/dist/providers/compat/qwen-compatibility.js.map +0 -1
  414. package/dist/providers/compat/register-compat-module.d.ts +0 -8
  415. package/dist/providers/compat/register-compat-module.js +0 -53
  416. package/dist/providers/compat/register-compat-module.js.map +0 -1
  417. package/dist/providers/compat/responses/c4m-responses-compatibility.d.ts +0 -27
  418. package/dist/providers/compat/responses/c4m-responses-compatibility.js +0 -197
  419. package/dist/providers/compat/responses/c4m-responses-compatibility.js.map +0 -1
  420. package/dist/providers/compat/standard-compatibility-utils.d.ts +0 -1
  421. package/dist/providers/compat/standard-compatibility-utils.js +0 -77
  422. package/dist/providers/compat/standard-compatibility-utils.js.map +0 -1
  423. package/dist/providers/compat/standard-compatibility.d.ts +0 -31
  424. package/dist/providers/compat/standard-compatibility.js +0 -118
  425. package/dist/providers/compat/standard-compatibility.js.map +0 -1
  426. package/dist/providers/compat/utils/snapshot-writer.js +0 -62
  427. package/dist/providers/compat/utils/snapshot-writer.js.map +0 -1
  428. package/dist/tools/replay-request.d.ts +0 -0
  429. package/dist/tools/replay-request.js +0 -2
  430. package/dist/tools/replay-request.js.map +0 -1
  431. package/scripts/check-glm-compat.mjs +0 -47
@@ -1,4 +1,5 @@
1
- import { VirtualRouterError, VirtualRouterErrorCode } from './types.js';
1
+ import { DEFAULT_MODEL_CONTEXT_TOKENS, VirtualRouterError, VirtualRouterErrorCode } from './types.js';
2
+ import { scanOAuthTokenFiles } from './token-file-scanner.js';
2
3
  const DEFAULT_CLASSIFIER = {
3
4
  longContextThresholdTokens: 180000,
4
5
  thinkingKeywords: ['think step', 'analysis', 'reasoning', '仔细分析', '深度思考'],
@@ -8,6 +9,10 @@ const DEFAULT_CLASSIFIER = {
8
9
  };
9
10
  const DEFAULT_LOAD_BALANCING = { strategy: 'round-robin' };
10
11
  const DEFAULT_HEALTH = { failureThreshold: 3, cooldownMs: 30_000, fatalCooldownMs: 300_000 };
12
+ const DEFAULT_CONTEXT_ROUTING = {
13
+ warnRatio: 0.9,
14
+ hardLimit: false
15
+ };
11
16
  /**
12
17
  * 将用户提供的 Virtual Router 配置(或包含 virtualrouter 字段的整体配置)
13
18
  * 规范化为 VirtualRouterConfig,供 HubPipeline / VirtualRouterEngine 直接使用。
@@ -22,6 +27,8 @@ export function bootstrapVirtualRouterConfig(input) {
22
27
  if (!Object.keys(routingSource).length) {
23
28
  throw new VirtualRouterError('Virtual Router routing table cannot be empty', VirtualRouterErrorCode.CONFIG_ERROR);
24
29
  }
30
+ const webSearch = normalizeWebSearch(section.webSearch, routingSource);
31
+ validateWebSearchRouting(webSearch, routingSource);
25
32
  const { runtimeEntries, aliasIndex } = buildProviderRuntimeEntries(providersSource);
26
33
  const { routing, targetKeys } = expandRoutingTable(routingSource, aliasIndex);
27
34
  if (!routing.default || routing.default.length === 0) {
@@ -31,12 +38,15 @@ export function bootstrapVirtualRouterConfig(input) {
31
38
  const classifier = normalizeClassifier(section.classifier);
32
39
  const loadBalancing = section.loadBalancing ?? DEFAULT_LOAD_BALANCING;
33
40
  const health = section.health ?? DEFAULT_HEALTH;
41
+ const contextRouting = section.contextRouting ?? DEFAULT_CONTEXT_ROUTING;
34
42
  const config = {
35
43
  routing,
36
44
  providers: providerProfiles,
37
45
  classifier,
38
46
  loadBalancing,
39
- health
47
+ health,
48
+ contextRouting,
49
+ ...(webSearch ? { webSearch } : {})
40
50
  };
41
51
  return {
42
52
  config,
@@ -56,7 +66,9 @@ function extractVirtualRouterSection(input) {
56
66
  const classifier = (section.classifier ?? root.classifier);
57
67
  const loadBalancing = normalizeLoadBalancing(section.loadBalancing ?? root.loadBalancing);
58
68
  const health = normalizeHealth(section.health ?? root.health);
59
- return { providers, routing, classifier, loadBalancing, health };
69
+ const contextRouting = normalizeContextRouting(section.contextRouting ?? root.contextRouting);
70
+ const webSearch = section.webSearch ?? root.webSearch;
71
+ return { providers, routing, classifier, loadBalancing, health, contextRouting, webSearch };
60
72
  }
61
73
  function buildProviderRuntimeEntries(providers) {
62
74
  const runtimeEntries = {};
@@ -86,6 +98,11 @@ function buildProviderRuntimeEntries(providers) {
86
98
  userInfoUrl: entry.auth.userInfoUrl,
87
99
  refreshUrl: entry.auth.refreshUrl
88
100
  };
101
+ // 为 OAuth 类型的 auth 设置 tokenFile 为 alias(如果没有显式配置 tokenFile)
102
+ // 这允许 oauth-lifecycle.ts 的 resolveTokenFilePath 函数正确解析并匹配现有文件
103
+ if (!runtimeAuth.tokenFile && (runtimeAuth.rawType?.includes('oauth') || runtimeAuth.type === 'oauth')) {
104
+ runtimeAuth.tokenFile = entry.keyAlias;
105
+ }
89
106
  if (runtimeAuth.type === 'apiKey' && !runtimeAuth.secretRef) {
90
107
  runtimeAuth.secretRef = `${providerId}.${entry.keyAlias}`;
91
108
  }
@@ -100,7 +117,12 @@ function buildProviderRuntimeEntries(providers) {
100
117
  outboundProfile: normalizedProvider.outboundProfile,
101
118
  compatibilityProfile: normalizedProvider.compatibilityProfile,
102
119
  processMode: normalizedProvider.processMode,
103
- responsesConfig: normalizedProvider.responsesConfig
120
+ responsesConfig: normalizedProvider.responsesConfig,
121
+ streaming: normalizedProvider.streaming,
122
+ modelStreaming: normalizedProvider.modelStreaming,
123
+ modelContextTokens: normalizedProvider.modelContextTokens,
124
+ defaultContextTokens: normalizedProvider.defaultContextTokens,
125
+ ...(normalizedProvider.serverToolsDisabled ? { serverToolsDisabled: true } : {})
104
126
  };
105
127
  }
106
128
  }
@@ -109,28 +131,40 @@ function buildProviderRuntimeEntries(providers) {
109
131
  function expandRoutingTable(routingSource, aliasIndex) {
110
132
  const routing = {};
111
133
  const targetKeys = new Set();
112
- for (const [routeName, entries] of Object.entries(routingSource)) {
113
- const expanded = [];
114
- for (const entry of entries) {
115
- const parsed = parseRouteEntry(entry, aliasIndex);
116
- if (!parsed) {
117
- continue;
118
- }
119
- if (!aliasIndex.has(parsed.providerId)) {
120
- throw new VirtualRouterError(`Route "${routeName}" references unknown provider "${parsed.providerId}"`, VirtualRouterErrorCode.CONFIG_ERROR);
121
- }
122
- const aliases = parsed.keyAlias ? [parsed.keyAlias] : aliasIndex.get(parsed.providerId);
123
- if (!aliases.length) {
124
- throw new VirtualRouterError(`Provider ${parsed.providerId} has no auth aliases but is referenced in routing`, VirtualRouterErrorCode.CONFIG_ERROR);
134
+ for (const [routeName, pools] of Object.entries(routingSource)) {
135
+ const expandedPools = [];
136
+ for (const pool of pools) {
137
+ const expandedTargets = [];
138
+ for (const entry of pool.targets) {
139
+ const parsed = parseRouteEntry(entry, aliasIndex);
140
+ if (!parsed) {
141
+ continue;
142
+ }
143
+ if (!aliasIndex.has(parsed.providerId)) {
144
+ throw new VirtualRouterError(`Route "${routeName}" references unknown provider "${parsed.providerId}"`, VirtualRouterErrorCode.CONFIG_ERROR);
145
+ }
146
+ const aliases = parsed.keyAlias ? [parsed.keyAlias] : aliasIndex.get(parsed.providerId);
147
+ if (!aliases.length) {
148
+ throw new VirtualRouterError(`Provider ${parsed.providerId} has no auth aliases but is referenced in routing`, VirtualRouterErrorCode.CONFIG_ERROR);
149
+ }
150
+ for (const alias of aliases) {
151
+ const runtimeKey = buildRuntimeKey(parsed.providerId, alias);
152
+ const targetKey = `${runtimeKey}.${parsed.modelId}`;
153
+ pushUnique(expandedTargets, targetKey);
154
+ targetKeys.add(targetKey);
155
+ }
125
156
  }
126
- for (const alias of aliases) {
127
- const runtimeKey = buildRuntimeKey(parsed.providerId, alias);
128
- const targetKey = `${runtimeKey}.${parsed.modelId}`;
129
- pushUnique(expanded, targetKey);
130
- targetKeys.add(targetKey);
157
+ if (expandedTargets.length) {
158
+ expandedPools.push({
159
+ id: pool.id,
160
+ priority: pool.priority,
161
+ backup: pool.backup,
162
+ targets: expandedTargets,
163
+ ...(pool.force ? { force: true } : {})
164
+ });
131
165
  }
132
166
  }
133
- routing[routeName] = expanded;
167
+ routing[routeName] = expandedPools;
134
168
  }
135
169
  return { routing, targetKeys };
136
170
  }
@@ -146,6 +180,10 @@ function buildProviderProfiles(targetKeys, runtimeEntries) {
146
180
  if (!runtime) {
147
181
  throw new VirtualRouterError(`Routing target ${targetKey} references unknown runtime key ${runtimeKey}`, VirtualRouterErrorCode.CONFIG_ERROR);
148
182
  }
183
+ const streamingPref = runtime.modelStreaming?.[parsed.modelId] !== undefined
184
+ ? runtime.modelStreaming?.[parsed.modelId]
185
+ : runtime.streaming;
186
+ const contextTokens = resolveContextTokens(runtime, parsed.modelId);
149
187
  profiles[targetKey] = {
150
188
  providerKey: targetKey,
151
189
  providerType: runtime.providerType,
@@ -156,27 +194,166 @@ function buildProviderProfiles(targetKeys, runtimeEntries) {
156
194
  runtimeKey,
157
195
  modelId: parsed.modelId,
158
196
  processMode: runtime.processMode || 'chat',
159
- responsesConfig: runtime.responsesConfig
197
+ responsesConfig: runtime.responsesConfig,
198
+ streaming: streamingPref,
199
+ maxContextTokens: contextTokens,
200
+ ...(runtime.serverToolsDisabled ? { serverToolsDisabled: true } : {})
160
201
  };
161
202
  targetRuntime[targetKey] = {
162
203
  ...runtime,
163
- modelId: parsed.modelId
204
+ modelId: parsed.modelId,
205
+ streaming: streamingPref,
206
+ maxContextTokens: contextTokens
164
207
  };
165
208
  }
166
209
  return { profiles, targetRuntime };
167
210
  }
211
+ function resolveContextTokens(runtime, modelId) {
212
+ const specific = runtime.modelContextTokens?.[modelId];
213
+ if (typeof specific === 'number' && Number.isFinite(specific) && specific > 0) {
214
+ return Math.floor(specific);
215
+ }
216
+ const fallback = runtime.defaultContextTokens ?? runtime.maxContextTokens;
217
+ if (typeof fallback === 'number' && Number.isFinite(fallback) && fallback > 0) {
218
+ return Math.floor(fallback);
219
+ }
220
+ return DEFAULT_MODEL_CONTEXT_TOKENS;
221
+ }
168
222
  function normalizeRouting(source) {
169
223
  const routing = {};
170
224
  for (const [routeName, entries] of Object.entries(source)) {
171
- if (!Array.isArray(entries))
225
+ if (!Array.isArray(entries) || !entries.length) {
226
+ routing[routeName] = [];
172
227
  continue;
173
- const normalized = entries
174
- .map((entry) => (typeof entry === 'string' ? entry.trim() : ''))
175
- .filter(Boolean);
176
- routing[routeName] = Array.from(new Set(normalized));
228
+ }
229
+ const allStrings = entries.every((entry) => typeof entry === 'string' || entry === null || entry === undefined);
230
+ if (allStrings) {
231
+ const targets = normalizeTargetList(entries);
232
+ routing[routeName] = targets.length ? [buildLegacyRoutePool(routeName, targets)] : [];
233
+ continue;
234
+ }
235
+ const normalized = [];
236
+ const total = entries.length || 1;
237
+ for (let index = 0; index < entries.length; index += 1) {
238
+ const entry = entries[index];
239
+ const pool = normalizeRoutePoolEntry(routeName, entry, index, total);
240
+ if (pool && pool.targets.length) {
241
+ normalized.push(pool);
242
+ }
243
+ }
244
+ routing[routeName] = normalized;
177
245
  }
178
246
  return routing;
179
247
  }
248
+ function buildLegacyRoutePool(routeName, targets) {
249
+ return {
250
+ id: `${routeName}:pool0`,
251
+ priority: targets.length,
252
+ backup: false,
253
+ targets
254
+ };
255
+ }
256
+ function normalizeRoutePoolEntry(routeName, entry, index, total) {
257
+ if (typeof entry === 'string') {
258
+ const targets = normalizeTargetList(entry);
259
+ return targets.length
260
+ ? {
261
+ id: `${routeName}:pool${index + 1}`,
262
+ priority: total - index,
263
+ backup: false,
264
+ targets
265
+ }
266
+ : null;
267
+ }
268
+ if (!entry || typeof entry !== 'object') {
269
+ return null;
270
+ }
271
+ const record = entry;
272
+ const id = readOptionalString(record.id) ??
273
+ readOptionalString(record?.poolId) ??
274
+ `${routeName}:pool${index + 1}`;
275
+ const backup = record.backup === true ||
276
+ record.isBackup === true ||
277
+ (typeof record.type === 'string' && record.type.toLowerCase() === 'backup');
278
+ const priority = normalizePriorityValue(record.priority, total - index);
279
+ const targets = normalizeRouteTargets(record);
280
+ const force = record.force === true ||
281
+ (typeof record.force === 'string' && record.force.trim().toLowerCase() === 'true');
282
+ return targets.length
283
+ ? {
284
+ id,
285
+ priority,
286
+ backup,
287
+ targets,
288
+ ...(force ? { force: true } : {})
289
+ }
290
+ : null;
291
+ }
292
+ function normalizeRouteTargets(record) {
293
+ const buckets = [
294
+ record.targets,
295
+ record.providers,
296
+ record.pool,
297
+ record.entries,
298
+ record.items,
299
+ record.routes
300
+ ];
301
+ const normalized = [];
302
+ for (const bucket of buckets) {
303
+ for (const target of normalizeTargetList(bucket)) {
304
+ if (!normalized.includes(target)) {
305
+ normalized.push(target);
306
+ }
307
+ }
308
+ }
309
+ const singular = [record.target, record.provider];
310
+ for (const candidate of singular) {
311
+ for (const target of normalizeTargetList(candidate)) {
312
+ if (!normalized.includes(target)) {
313
+ normalized.push(target);
314
+ }
315
+ }
316
+ }
317
+ return normalized;
318
+ }
319
+ function normalizeTargetList(value) {
320
+ if (Array.isArray(value)) {
321
+ const normalized = [];
322
+ for (const entry of value) {
323
+ if (typeof entry === 'string') {
324
+ const trimmed = entry.trim();
325
+ if (trimmed && !normalized.includes(trimmed)) {
326
+ normalized.push(trimmed);
327
+ }
328
+ }
329
+ }
330
+ return normalized;
331
+ }
332
+ if (typeof value === 'string') {
333
+ const trimmed = value.trim();
334
+ return trimmed ? [trimmed] : [];
335
+ }
336
+ if (typeof value === 'number') {
337
+ const str = String(value).trim();
338
+ return str ? [str] : [];
339
+ }
340
+ return [];
341
+ }
342
+ function normalizePriorityValue(value, fallback) {
343
+ if (typeof value === 'number' && Number.isFinite(value)) {
344
+ return value;
345
+ }
346
+ if (typeof value === 'string') {
347
+ const trimmed = value.trim();
348
+ if (trimmed) {
349
+ const parsed = Number(trimmed);
350
+ if (Number.isFinite(parsed)) {
351
+ return parsed;
352
+ }
353
+ }
354
+ }
355
+ return fallback;
356
+ }
180
357
  function normalizeClassifier(input) {
181
358
  const normalized = asRecord(input);
182
359
  const result = {
@@ -209,8 +386,18 @@ function normalizeProvider(providerId, raw) {
209
386
  : '';
210
387
  const headers = normalizeHeaders(provider.headers);
211
388
  const compatibilityProfile = resolveCompatibilityProfile(providerId, provider);
212
- const responsesConfig = normalizeResponsesConfig(provider);
389
+ const responsesNode = asRecord(provider.responses);
390
+ const responsesConfig = normalizeResponsesConfig(provider, responsesNode);
213
391
  const processMode = normalizeProcessMode(provider.process);
392
+ const streaming = resolveProviderStreamingPreference(provider, responsesNode);
393
+ const modelStreaming = normalizeModelStreaming(provider);
394
+ const { modelContextTokens, defaultContextTokens } = normalizeModelContextTokens(provider);
395
+ const serverToolsDisabled = provider.serverToolsDisabled === true ||
396
+ (typeof provider.serverToolsDisabled === 'string' &&
397
+ provider.serverToolsDisabled.trim().toLowerCase() === 'true') ||
398
+ (provider.serverTools &&
399
+ typeof provider.serverTools === 'object' &&
400
+ provider.serverTools.enabled === false);
214
401
  return {
215
402
  providerId,
216
403
  providerType,
@@ -219,20 +406,106 @@ function normalizeProvider(providerId, raw) {
219
406
  outboundProfile: mapOutboundProfile(providerType),
220
407
  compatibilityProfile,
221
408
  processMode,
222
- responsesConfig
409
+ responsesConfig,
410
+ streaming,
411
+ modelStreaming,
412
+ modelContextTokens,
413
+ defaultContextTokens,
414
+ ...(serverToolsDisabled ? { serverToolsDisabled: true } : {})
415
+ };
416
+ }
417
+ function normalizeModelStreaming(provider) {
418
+ const modelsNode = asRecord(provider.models);
419
+ if (!modelsNode) {
420
+ return undefined;
421
+ }
422
+ const normalized = {};
423
+ for (const [modelId, modelRaw] of Object.entries(modelsNode)) {
424
+ if (!modelRaw || typeof modelRaw !== 'object') {
425
+ continue;
426
+ }
427
+ const preference = resolveStreamingPreference(modelRaw);
428
+ if (preference) {
429
+ normalized[modelId] = preference;
430
+ }
431
+ }
432
+ return Object.keys(normalized).length ? normalized : undefined;
433
+ }
434
+ function normalizeModelContextTokens(provider) {
435
+ const modelsNode = asRecord(provider.models);
436
+ const normalized = {};
437
+ for (const [modelId, modelRaw] of Object.entries(modelsNode)) {
438
+ if (!modelRaw || typeof modelRaw !== 'object') {
439
+ continue;
440
+ }
441
+ const candidate = readContextTokens(modelRaw);
442
+ if (candidate) {
443
+ normalized[modelId] = candidate;
444
+ }
445
+ }
446
+ const configNode = asRecord(provider.config);
447
+ const defaultsNode = asRecord(configNode?.userConfigDefaults);
448
+ const defaultCandidate = readContextTokens(provider) ??
449
+ readContextTokens(configNode) ??
450
+ readContextTokens(defaultsNode);
451
+ return {
452
+ modelContextTokens: Object.keys(normalized).length ? normalized : undefined,
453
+ defaultContextTokens: defaultCandidate
223
454
  };
224
455
  }
225
- function normalizeResponsesConfig(provider) {
226
- const node = asRecord(provider.responses);
227
- if (!node) {
456
+ function resolveStreamingPreference(model) {
457
+ return (coerceStreamingPreference(model.streaming) ??
458
+ coerceStreamingPreference(model.stream) ??
459
+ coerceStreamingPreference(model.supportsStreaming));
460
+ }
461
+ function coerceStreamingPreference(value) {
462
+ if (typeof value === 'string') {
463
+ const normalized = value.trim().toLowerCase();
464
+ if (normalized === 'always' || normalized === 'auto' || normalized === 'never') {
465
+ return normalized;
466
+ }
467
+ if (normalized === 'true') {
468
+ return 'always';
469
+ }
470
+ if (normalized === 'false') {
471
+ return 'never';
472
+ }
473
+ }
474
+ if (typeof value === 'boolean') {
475
+ return value ? 'always' : 'never';
476
+ }
477
+ if (value && typeof value === 'object') {
478
+ const record = value;
479
+ if (record.mode !== undefined) {
480
+ return coerceStreamingPreference(record.mode);
481
+ }
482
+ if (record.value !== undefined) {
483
+ return coerceStreamingPreference(record.value);
484
+ }
485
+ if (record.enabled !== undefined) {
486
+ return coerceStreamingPreference(record.enabled);
487
+ }
488
+ }
489
+ return undefined;
490
+ }
491
+ function normalizeResponsesConfig(provider, node) {
492
+ const source = node ?? asRecord(provider.responses);
493
+ if (!source) {
228
494
  return undefined;
229
495
  }
230
- const rawStyle = typeof node.toolCallIdStyle === 'string' ? node.toolCallIdStyle.trim().toLowerCase() : undefined;
496
+ const rawStyle = typeof source.toolCallIdStyle === 'string' ? source.toolCallIdStyle.trim().toLowerCase() : undefined;
231
497
  if (rawStyle === 'fc' || rawStyle === 'preserve') {
232
498
  return { toolCallIdStyle: rawStyle };
233
499
  }
234
500
  return undefined;
235
501
  }
502
+ function resolveProviderStreamingPreference(provider, responsesNode) {
503
+ const configNode = asRecord(provider.config);
504
+ const configResponses = configNode ? asRecord(configNode.responses) : undefined;
505
+ return (coerceStreamingPreference(provider.streaming ?? provider.stream ?? provider.supportsStreaming ?? provider.streamingPreference) ??
506
+ coerceStreamingPreference(responsesNode?.streaming ?? responsesNode?.stream ?? responsesNode?.supportsStreaming) ??
507
+ coerceStreamingPreference(configResponses?.streaming ?? configResponses?.stream));
508
+ }
236
509
  function resolveCompatibilityProfile(providerId, provider) {
237
510
  if (typeof provider.compatibilityProfile === 'string' && provider.compatibilityProfile.trim()) {
238
511
  return provider.compatibilityProfile.trim();
@@ -259,6 +532,121 @@ function normalizeProcessMode(value) {
259
532
  }
260
533
  return 'chat';
261
534
  }
535
+ function normalizeContextRouting(input) {
536
+ if (!input || typeof input !== 'object') {
537
+ return { ...DEFAULT_CONTEXT_ROUTING };
538
+ }
539
+ const record = input;
540
+ const warnCandidate = coerceRatio(record.warnRatio) ??
541
+ coerceRatio(record?.warn_ratio);
542
+ const hardLimitCandidate = coerceBoolean(record.hardLimit) ??
543
+ coerceBoolean(record?.hard_limit);
544
+ const warnRatio = clampWarnRatio(warnCandidate ?? DEFAULT_CONTEXT_ROUTING.warnRatio);
545
+ const hardLimit = typeof hardLimitCandidate === 'boolean' ? hardLimitCandidate : DEFAULT_CONTEXT_ROUTING.hardLimit;
546
+ return {
547
+ warnRatio,
548
+ hardLimit
549
+ };
550
+ }
551
+ function validateWebSearchRouting(webSearch, routingSource) {
552
+ if (!webSearch) {
553
+ return;
554
+ }
555
+ const routePools = routingSource['web_search'] ?? routingSource['search'];
556
+ if (!Array.isArray(routePools) || !routePools.length) {
557
+ throw new VirtualRouterError('Virtual Router webSearch.engines configured but routing.web_search (or search) route is missing or empty', VirtualRouterErrorCode.CONFIG_ERROR);
558
+ }
559
+ const targets = new Set();
560
+ for (const pool of routePools) {
561
+ if (!pool || !Array.isArray(pool.targets)) {
562
+ continue;
563
+ }
564
+ for (const target of pool.targets) {
565
+ if (typeof target === 'string' && target.trim()) {
566
+ targets.add(target.trim());
567
+ }
568
+ }
569
+ }
570
+ for (const engine of webSearch.engines) {
571
+ if (!targets.has(engine.providerKey)) {
572
+ throw new VirtualRouterError(`Virtual Router webSearch engine "${engine.id}" references providerKey "${engine.providerKey}" which is not present in routing.web_search/search`, VirtualRouterErrorCode.CONFIG_ERROR);
573
+ }
574
+ }
575
+ }
576
+ function normalizeWebSearch(input, routingSource) {
577
+ if (!input || typeof input !== 'object') {
578
+ return undefined;
579
+ }
580
+ const record = input;
581
+ const enginesNode = Array.isArray(record.engines) ? record.engines : [];
582
+ const engines = [];
583
+ for (const raw of enginesNode) {
584
+ if (!raw || typeof raw !== 'object') {
585
+ continue;
586
+ }
587
+ const node = raw;
588
+ const idRaw = node.id;
589
+ const providerKeyRaw = node.providerKey ?? node.provider ?? node.target;
590
+ const id = typeof idRaw === 'string' && idRaw.trim()
591
+ ? idRaw.trim()
592
+ : undefined;
593
+ const providerKey = typeof providerKeyRaw === 'string' && providerKeyRaw.trim()
594
+ ? providerKeyRaw.trim()
595
+ : undefined;
596
+ if (!id || !providerKey) {
597
+ continue;
598
+ }
599
+ const description = typeof node.description === 'string' && node.description.trim()
600
+ ? node.description.trim()
601
+ : undefined;
602
+ const isDefault = node.default === true ||
603
+ (typeof node.default === 'string' && node.default.trim().toLowerCase() === 'true');
604
+ const serverToolsDisabled = node.serverToolsDisabled === true ||
605
+ (typeof node.serverToolsDisabled === 'string' &&
606
+ node.serverToolsDisabled.trim().toLowerCase() === 'true') ||
607
+ (node.serverTools &&
608
+ typeof node.serverTools === 'object' &&
609
+ node.serverTools.enabled === false);
610
+ // Deduplicate by id; first wins, subsequent are ignored.
611
+ if (engines.some((engine) => engine.id === id)) {
612
+ continue;
613
+ }
614
+ engines.push({
615
+ id,
616
+ providerKey,
617
+ description,
618
+ default: isDefault,
619
+ ...(serverToolsDisabled ? { serverToolsDisabled: true } : {})
620
+ });
621
+ }
622
+ if (!engines.length) {
623
+ return undefined;
624
+ }
625
+ let injectPolicy;
626
+ let force;
627
+ const rawPolicy = record.injectPolicy ?? record?.inject_policy;
628
+ if (typeof rawPolicy === 'string') {
629
+ const normalized = rawPolicy.trim().toLowerCase();
630
+ if (normalized === 'always' || normalized === 'selective') {
631
+ injectPolicy = normalized;
632
+ }
633
+ }
634
+ if (record.force === true ||
635
+ (typeof record.force === 'string' && record.force.trim().toLowerCase() === 'true')) {
636
+ force = true;
637
+ }
638
+ else {
639
+ const webSearchPools = routingSource['web_search'] ?? routingSource['search'] ?? [];
640
+ if (Array.isArray(webSearchPools) && webSearchPools.some((pool) => pool.force)) {
641
+ force = true;
642
+ }
643
+ }
644
+ return {
645
+ engines,
646
+ injectPolicy: injectPolicy ?? 'selective',
647
+ ...(force ? { force } : {})
648
+ };
649
+ }
262
650
  function extractProviderAuthEntries(providerId, raw) {
263
651
  const provider = asRecord(raw);
264
652
  const auth = asRecord(provider.auth);
@@ -389,6 +777,43 @@ function extractProviderAuthEntries(providerId, raw) {
389
777
  else if (typeof apiKeyField === 'string' && apiKeyField.trim()) {
390
778
  pushEntry(undefined, buildAuthCandidate(baseTypeSource, { value: apiKeyField.trim() }));
391
779
  }
780
+ // 自动多 token 扫描:仅在未显式声明多 key、且为受支持的 OAuth 提供方时触发
781
+ if (baseType === 'oauth') {
782
+ const scanCandidates = new Set();
783
+ const pushCandidate = (value) => {
784
+ if (typeof value === 'string' && value.trim()) {
785
+ scanCandidates.add(value.trim().toLowerCase());
786
+ }
787
+ };
788
+ pushCandidate(auth?.oauthProviderId);
789
+ pushCandidate(baseTypeInfo.oauthProviderId);
790
+ pushCandidate(providerId);
791
+ for (const candidate of scanCandidates) {
792
+ if (!MULTI_TOKEN_OAUTH_PROVIDERS.has(candidate)) {
793
+ continue;
794
+ }
795
+ const tokenFiles = scanOAuthTokenFiles(candidate);
796
+ if (!tokenFiles.length) {
797
+ continue;
798
+ }
799
+ const baseTypeAlias = baseTypeInfo.oauthProviderId?.toLowerCase();
800
+ for (const match of tokenFiles) {
801
+ const alias = match.alias && match.alias !== 'default'
802
+ ? `${match.sequence}-${match.alias}`
803
+ : String(match.sequence);
804
+ const typeHint = baseTypeSource && baseTypeAlias === candidate
805
+ ? baseTypeSource
806
+ : `${candidate}-oauth`;
807
+ const authConfig = {
808
+ ...defaults,
809
+ type: typeHint,
810
+ tokenFile: match.filePath,
811
+ oauthProviderId: candidate
812
+ };
813
+ pushEntry(alias, authConfig);
814
+ }
815
+ }
816
+ }
392
817
  if (!entries.length) {
393
818
  const fallbackExtras = {
394
819
  value: readOptionalString(auth.value),
@@ -552,6 +977,7 @@ function mergeScopes(primary, fallback) {
552
977
  }
553
978
  return merged.size ? Array.from(merged) : undefined;
554
979
  }
980
+ const MULTI_TOKEN_OAUTH_PROVIDERS = new Set(['iflow', 'qwen', 'gemini-cli', 'antigravity']);
555
981
  function interpretAuthType(value) {
556
982
  if (typeof value !== 'string') {
557
983
  return { type: 'apiKey' };
@@ -595,6 +1021,47 @@ function normalizeLoadBalancing(input) {
595
1021
  ? { strategy, weights: weightsEntries }
596
1022
  : { strategy };
597
1023
  }
1024
+ function coerceRatio(value) {
1025
+ if (typeof value === 'number' && Number.isFinite(value)) {
1026
+ return value;
1027
+ }
1028
+ if (typeof value === 'string') {
1029
+ const trimmed = value.trim();
1030
+ if (!trimmed) {
1031
+ return undefined;
1032
+ }
1033
+ const parsed = Number(trimmed);
1034
+ if (Number.isFinite(parsed)) {
1035
+ return parsed;
1036
+ }
1037
+ }
1038
+ return undefined;
1039
+ }
1040
+ function clampWarnRatio(value) {
1041
+ if (!Number.isFinite(value)) {
1042
+ return DEFAULT_CONTEXT_ROUTING.warnRatio;
1043
+ }
1044
+ const clamped = Math.max(0.1, Math.min(value, 0.99));
1045
+ return Number.isFinite(clamped) ? clamped : DEFAULT_CONTEXT_ROUTING.warnRatio;
1046
+ }
1047
+ function coerceBoolean(value) {
1048
+ if (typeof value === 'boolean') {
1049
+ return value;
1050
+ }
1051
+ if (typeof value === 'string') {
1052
+ const normalized = value.trim().toLowerCase();
1053
+ if (!normalized) {
1054
+ return undefined;
1055
+ }
1056
+ if (['true', '1', 'yes', 'y'].includes(normalized)) {
1057
+ return true;
1058
+ }
1059
+ if (['false', '0', 'no', 'n'].includes(normalized)) {
1060
+ return false;
1061
+ }
1062
+ }
1063
+ return undefined;
1064
+ }
598
1065
  function normalizeHealth(input) {
599
1066
  if (!input || typeof input !== 'object')
600
1067
  return undefined;
@@ -609,6 +1076,43 @@ function normalizeHealth(input) {
609
1076
  ? { failureThreshold, cooldownMs, fatalCooldownMs }
610
1077
  : { failureThreshold, cooldownMs };
611
1078
  }
1079
+ function readContextTokens(record) {
1080
+ if (!record) {
1081
+ return undefined;
1082
+ }
1083
+ const keys = [
1084
+ 'maxContextTokens',
1085
+ 'max_context_tokens',
1086
+ 'maxContext',
1087
+ 'max_context',
1088
+ 'contextTokens',
1089
+ 'context_tokens'
1090
+ ];
1091
+ for (const key of keys) {
1092
+ const value = record[key];
1093
+ const parsed = normalizePositiveInteger(value);
1094
+ if (parsed) {
1095
+ return parsed;
1096
+ }
1097
+ }
1098
+ return undefined;
1099
+ }
1100
+ function normalizePositiveInteger(value) {
1101
+ if (typeof value === 'number' && Number.isFinite(value) && value > 0) {
1102
+ return Math.floor(value);
1103
+ }
1104
+ if (typeof value === 'string') {
1105
+ const trimmed = value.trim();
1106
+ if (!trimmed) {
1107
+ return undefined;
1108
+ }
1109
+ const parsed = Number(trimmed);
1110
+ if (Number.isFinite(parsed) && parsed > 0) {
1111
+ return Math.floor(parsed);
1112
+ }
1113
+ }
1114
+ return undefined;
1115
+ }
612
1116
  function normalizeHeaders(input) {
613
1117
  if (!input || typeof input !== 'object') {
614
1118
  return undefined;