@jsonstudio/rcc 0.89.3 → 0.89.164

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 (270) hide show
  1. package/README.md +240 -179
  2. package/config/modules.json +1 -11
  3. package/dist/build-info.js +2 -2
  4. package/dist/build-info.js.map +1 -1
  5. package/dist/client/gemini-cli/gemini-cli-protocol-client.d.ts +16 -0
  6. package/dist/client/gemini-cli/gemini-cli-protocol-client.js +56 -0
  7. package/dist/client/gemini-cli/gemini-cli-protocol-client.js.map +1 -0
  8. package/dist/client/openai/chat-protocol-client.js.map +1 -1
  9. package/dist/config/modules.json +1 -11
  10. package/dist/core/provider-health-manager.d.ts +17 -0
  11. package/dist/core/provider-health-manager.js +66 -0
  12. package/dist/core/provider-health-manager.js.map +1 -0
  13. package/dist/error-handling/route-error-hub.d.ts +48 -0
  14. package/dist/error-handling/route-error-hub.js +131 -0
  15. package/dist/error-handling/route-error-hub.js.map +1 -0
  16. package/dist/index.js +26 -1
  17. package/dist/index.js.map +1 -1
  18. package/dist/modules/llmswitch/bridge.d.ts +2 -0
  19. package/dist/modules/llmswitch/bridge.js +17 -0
  20. package/dist/modules/llmswitch/bridge.js.map +1 -1
  21. package/dist/modules/pipeline/utils/colored-logger.d.ts +14 -0
  22. package/dist/modules/pipeline/utils/colored-logger.js +48 -0
  23. package/dist/modules/pipeline/utils/colored-logger.js.map +1 -0
  24. package/dist/modules/pipeline/utils/debug-logger.d.ts +2 -0
  25. package/dist/modules/pipeline/utils/debug-logger.js +36 -0
  26. package/dist/modules/pipeline/utils/debug-logger.js.map +1 -1
  27. package/dist/providers/auth/gemini-cli-userinfo-helper.d.ts +53 -0
  28. package/dist/providers/auth/gemini-cli-userinfo-helper.js +152 -0
  29. package/dist/providers/auth/gemini-cli-userinfo-helper.js.map +1 -0
  30. package/dist/providers/auth/oauth-auth.js +3 -2
  31. package/dist/providers/auth/oauth-auth.js.map +1 -1
  32. package/dist/providers/auth/oauth-lifecycle.js +21 -20
  33. package/dist/providers/auth/oauth-lifecycle.js.map +1 -1
  34. package/dist/providers/auth/oauth-logger.d.ts +1 -0
  35. package/dist/providers/auth/oauth-logger.js +21 -0
  36. package/dist/providers/auth/oauth-logger.js.map +1 -0
  37. package/dist/providers/compat/compat-directory-loader.js +2 -55
  38. package/dist/providers/compat/compat-directory-loader.js.map +1 -1
  39. package/dist/providers/compat/compatibility-factory.d.ts +4 -4
  40. package/dist/providers/compat/compatibility-factory.js +108 -0
  41. package/dist/providers/compat/compatibility-factory.js.map +1 -1
  42. package/dist/providers/compat/glm/glm-compatibility.d.ts +2 -2
  43. package/dist/providers/compat/glm/glm-compatibility.js +7 -7
  44. package/dist/providers/compat/glm/glm-compatibility.js.map +1 -1
  45. package/dist/providers/compat/glm/index.js +0 -6
  46. package/dist/providers/compat/glm/index.js.map +1 -1
  47. package/dist/providers/compat/iflow/iflow-compatibility.d.ts +1 -1
  48. package/dist/providers/compat/iflow/iflow-compatibility.js +6 -6
  49. package/dist/providers/compat/iflow/iflow-compatibility.js.map +1 -1
  50. package/dist/providers/compat/index.d.ts +0 -6
  51. package/dist/providers/compat/index.js +0 -7
  52. package/dist/providers/compat/index.js.map +1 -1
  53. package/dist/providers/compat/lmstudio-compatibility.d.ts +2 -2
  54. package/dist/providers/compat/lmstudio-compatibility.js +4 -4
  55. package/dist/providers/compat/lmstudio-compatibility.js.map +1 -1
  56. package/dist/providers/compat/passthrough-compatibility.d.ts +1 -1
  57. package/dist/providers/compat/passthrough-compatibility.js +3 -3
  58. package/dist/providers/compat/passthrough-compatibility.js.map +1 -1
  59. package/dist/providers/compat/profiles/chat/glm/index.d.ts +6 -0
  60. package/dist/providers/compat/profiles/chat/glm/index.js +6 -0
  61. package/dist/providers/compat/profiles/chat/glm/index.js.map +1 -0
  62. package/dist/providers/compat/profiles/chat/iflow/index.d.ts +6 -0
  63. package/dist/providers/compat/profiles/chat/iflow/index.js +6 -0
  64. package/dist/providers/compat/profiles/chat/iflow/index.js.map +1 -0
  65. package/dist/providers/compat/profiles/chat/lmstudio/index.d.ts +6 -0
  66. package/dist/providers/compat/profiles/chat/lmstudio/index.js +6 -0
  67. package/dist/providers/compat/profiles/chat/lmstudio/index.js.map +1 -0
  68. package/dist/providers/compat/profiles/chat/qwen/index.d.ts +6 -0
  69. package/dist/providers/compat/profiles/chat/qwen/index.js +6 -0
  70. package/dist/providers/compat/profiles/chat/qwen/index.js.map +1 -0
  71. package/dist/providers/compat/profiles/compat/passthrough/index.d.ts +6 -0
  72. package/dist/providers/compat/profiles/compat/passthrough/index.js +6 -0
  73. package/dist/providers/compat/profiles/compat/passthrough/index.js.map +1 -0
  74. package/dist/providers/compat/profiles/responses/c4m/index.d.ts +6 -0
  75. package/dist/providers/compat/profiles/responses/c4m/index.js +6 -0
  76. package/dist/providers/compat/profiles/responses/c4m/index.js.map +1 -0
  77. package/dist/providers/compat/profiles/responses/default/index.d.ts +6 -0
  78. package/dist/providers/compat/profiles/responses/default/index.js +6 -0
  79. package/dist/providers/compat/profiles/responses/default/index.js.map +1 -0
  80. package/dist/providers/compat/profiles/responses/fai/index.d.ts +6 -0
  81. package/dist/providers/compat/profiles/responses/fai/index.js +6 -0
  82. package/dist/providers/compat/profiles/responses/fai/index.js.map +1 -0
  83. package/dist/providers/compat/profiles/responses/fc/index.d.ts +6 -0
  84. package/dist/providers/compat/profiles/responses/fc/index.js +6 -0
  85. package/dist/providers/compat/profiles/responses/fc/index.js.map +1 -0
  86. package/dist/providers/compat/qwen/index.js +0 -6
  87. package/dist/providers/compat/qwen/index.js.map +1 -1
  88. package/dist/providers/compat/qwen-compatibility.d.ts +2 -2
  89. package/dist/providers/compat/qwen-compatibility.js +4 -4
  90. package/dist/providers/compat/qwen-compatibility.js.map +1 -1
  91. package/dist/providers/compat/register-compat-module.d.ts +8 -0
  92. package/dist/providers/compat/register-compat-module.js +53 -0
  93. package/dist/providers/compat/register-compat-module.js.map +1 -0
  94. package/dist/providers/compat/responses/c4m-responses-compatibility.d.ts +6 -2
  95. package/dist/providers/compat/responses/c4m-responses-compatibility.js +85 -3
  96. package/dist/providers/compat/responses/c4m-responses-compatibility.js.map +1 -1
  97. package/dist/providers/compat/standard-compatibility-utils.js +45 -15
  98. package/dist/providers/compat/standard-compatibility-utils.js.map +1 -1
  99. package/dist/providers/core/api/provider-config.d.ts +1 -1
  100. package/dist/providers/core/api/provider-types.d.ts +3 -1
  101. package/dist/providers/core/api/provider-types.js +1 -0
  102. package/dist/providers/core/api/provider-types.js.map +1 -1
  103. package/dist/providers/core/config/service-profiles.js +5 -2
  104. package/dist/providers/core/config/service-profiles.js.map +1 -1
  105. package/dist/providers/core/runtime/base-provider.d.ts +3 -0
  106. package/dist/providers/core/runtime/base-provider.js +101 -6
  107. package/dist/providers/core/runtime/base-provider.js.map +1 -1
  108. package/dist/providers/core/runtime/gemini-cli-http-provider.d.ts +34 -0
  109. package/dist/providers/core/runtime/gemini-cli-http-provider.js +152 -0
  110. package/dist/providers/core/runtime/gemini-cli-http-provider.js.map +1 -0
  111. package/dist/providers/core/runtime/http-transport-provider.d.ts +1 -0
  112. package/dist/providers/core/runtime/http-transport-provider.js +178 -123
  113. package/dist/providers/core/runtime/http-transport-provider.js.map +1 -1
  114. package/dist/providers/core/runtime/provider-factory.d.ts +1 -1
  115. package/dist/providers/core/runtime/provider-factory.js +8 -0
  116. package/dist/providers/core/runtime/provider-factory.js.map +1 -1
  117. package/dist/providers/core/runtime/provider-runtime-metadata.d.ts +1 -0
  118. package/dist/providers/core/runtime/provider-runtime-metadata.js.map +1 -1
  119. package/dist/providers/core/runtime/responses-provider.d.ts +7 -0
  120. package/dist/providers/core/runtime/responses-provider.js +228 -12
  121. package/dist/providers/core/runtime/responses-provider.js.map +1 -1
  122. package/dist/providers/core/strategies/oauth-auth-code-flow.js +10 -9
  123. package/dist/providers/core/strategies/oauth-auth-code-flow.js.map +1 -1
  124. package/dist/providers/core/strategies/oauth-device-flow.js +10 -9
  125. package/dist/providers/core/strategies/oauth-device-flow.js.map +1 -1
  126. package/dist/providers/core/utils/provider-error-reporter.js +63 -15
  127. package/dist/providers/core/utils/provider-error-reporter.js.map +1 -1
  128. package/dist/providers/core/utils/provider-type-utils.d.ts +1 -1
  129. package/dist/providers/core/utils/provider-type-utils.js +6 -1
  130. package/dist/providers/core/utils/provider-type-utils.js.map +1 -1
  131. package/dist/providers/core/utils/snapshot-writer.d.ts +10 -0
  132. package/dist/providers/core/utils/snapshot-writer.js +85 -0
  133. package/dist/providers/core/utils/snapshot-writer.js.map +1 -1
  134. package/dist/providers/mock/mock-provider-runtime.js +44 -0
  135. package/dist/providers/mock/mock-provider-runtime.js.map +1 -1
  136. package/dist/providers/profile/provider-profile-loader.js +26 -19
  137. package/dist/providers/profile/provider-profile-loader.js.map +1 -1
  138. package/dist/providers/profile/provider-profile.d.ts +2 -2
  139. package/dist/server/handlers/chat-handler.js +9 -3
  140. package/dist/server/handlers/chat-handler.js.map +1 -1
  141. package/dist/server/handlers/handler-utils.d.ts +7 -1
  142. package/dist/server/handlers/handler-utils.js +64 -52
  143. package/dist/server/handlers/handler-utils.js.map +1 -1
  144. package/dist/server/handlers/messages-handler.js +9 -3
  145. package/dist/server/handlers/messages-handler.js.map +1 -1
  146. package/dist/server/handlers/responses-handler.js +21 -13
  147. package/dist/server/handlers/responses-handler.js.map +1 -1
  148. package/dist/server/runtime/http-server/colored-logger.d.ts +1 -0
  149. package/dist/server/runtime/http-server/colored-logger.js +33 -0
  150. package/dist/server/runtime/http-server/colored-logger.js.map +1 -0
  151. package/dist/server/runtime/http-server/index.d.ts +3 -0
  152. package/dist/server/runtime/http-server/index.js +76 -19
  153. package/dist/server/runtime/http-server/index.js.map +1 -1
  154. package/dist/server/runtime/http-server/provider-utils.d.ts +3 -1
  155. package/dist/server/runtime/http-server/provider-utils.js +12 -2
  156. package/dist/server/runtime/http-server/provider-utils.js.map +1 -1
  157. package/dist/server/runtime/http-server/request-executor.js +6 -2
  158. package/dist/server/runtime/http-server/request-executor.js.map +1 -1
  159. package/dist/server/runtime/http-server/routes.js +31 -11
  160. package/dist/server/runtime/http-server/routes.js.map +1 -1
  161. package/dist/server/runtime/http-server/types.d.ts +2 -1
  162. package/dist/utils/error-center-payload.d.ts +7 -0
  163. package/dist/utils/error-center-payload.js +67 -0
  164. package/dist/utils/error-center-payload.js.map +1 -0
  165. package/dist/utils/error-handler-registry.d.ts +7 -0
  166. package/dist/utils/error-handler-registry.js +44 -12
  167. package/dist/utils/error-handler-registry.js.map +1 -1
  168. package/node_modules/@jsonstudio/llms/dist/conversion/codecs/responses-openai-codec.js +16 -1
  169. package/node_modules/@jsonstudio/llms/dist/conversion/compat/profiles/chat-glm.json +17 -0
  170. package/node_modules/@jsonstudio/llms/dist/conversion/compat/profiles/chat-iflow.json +36 -0
  171. package/node_modules/@jsonstudio/llms/dist/conversion/compat/profiles/chat-lmstudio.json +37 -0
  172. package/node_modules/@jsonstudio/llms/dist/conversion/compat/profiles/chat-qwen.json +18 -0
  173. package/node_modules/@jsonstudio/llms/dist/conversion/compat/profiles/responses-c4m.json +45 -0
  174. package/node_modules/@jsonstudio/llms/dist/conversion/config/compat-profiles.json +38 -0
  175. package/node_modules/@jsonstudio/llms/dist/conversion/config/sample-config.json +314 -0
  176. package/node_modules/@jsonstudio/llms/dist/conversion/config/version-switch.json +150 -0
  177. package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/compat/compat-engine.d.ts +4 -0
  178. package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/compat/compat-engine.js +667 -0
  179. package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/compat/compat-profile-store.d.ts +2 -0
  180. package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/compat/compat-profile-store.js +76 -0
  181. package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/compat/compat-types.d.ts +62 -0
  182. package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/compat/compat-types.js +1 -0
  183. package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/hub-pipeline.d.ts +2 -0
  184. package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/hub-pipeline.js +110 -29
  185. package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage3_compat/index.d.ts +14 -0
  186. package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage3_compat/index.js +23 -0
  187. package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/stages/resp_outbound/resp_outbound_stage1_client_remap/index.js +34 -0
  188. package/node_modules/@jsonstudio/llms/dist/conversion/hub/process/chat-process.js +4 -1
  189. package/node_modules/@jsonstudio/llms/dist/conversion/hub/response/provider-response.js +26 -0
  190. package/node_modules/@jsonstudio/llms/dist/conversion/responses/responses-openai-bridge.d.ts +1 -0
  191. package/node_modules/@jsonstudio/llms/dist/conversion/responses/responses-openai-bridge.js +71 -0
  192. package/node_modules/@jsonstudio/llms/dist/conversion/shared/responses-conversation-store.d.ts +35 -0
  193. package/node_modules/@jsonstudio/llms/dist/conversion/shared/responses-conversation-store.js +64 -19
  194. package/node_modules/@jsonstudio/llms/dist/conversion/shared/tool-filter-pipeline.d.ts +21 -0
  195. package/node_modules/@jsonstudio/llms/dist/conversion/shared/tool-filter-pipeline.js +138 -22
  196. package/node_modules/@jsonstudio/llms/dist/conversion/shared/tool-governor.d.ts +21 -0
  197. package/node_modules/@jsonstudio/llms/dist/conversion/shared/tool-governor.js +116 -1
  198. package/node_modules/@jsonstudio/llms/dist/conversion/shared/tool-mapping.js +52 -2
  199. package/node_modules/@jsonstudio/llms/dist/filters/config/openai-openai.fieldmap.json +18 -0
  200. package/node_modules/@jsonstudio/llms/dist/filters/special/request-tools-normalize.js +20 -1
  201. package/node_modules/@jsonstudio/llms/dist/guidance/index.js +6 -2
  202. package/node_modules/@jsonstudio/llms/dist/router/virtual-router/bootstrap.js +16 -7
  203. package/node_modules/@jsonstudio/llms/dist/router/virtual-router/classifier.js +40 -37
  204. package/node_modules/@jsonstudio/llms/dist/router/virtual-router/default-thinking-keywords.d.ts +1 -0
  205. package/node_modules/@jsonstudio/llms/dist/router/virtual-router/default-thinking-keywords.js +13 -0
  206. package/node_modules/@jsonstudio/llms/dist/router/virtual-router/engine.d.ts +39 -0
  207. package/node_modules/@jsonstudio/llms/dist/router/virtual-router/engine.js +52 -11
  208. package/node_modules/@jsonstudio/llms/dist/router/virtual-router/features.js +340 -11
  209. package/node_modules/@jsonstudio/llms/dist/router/virtual-router/token-counter.d.ts +2 -0
  210. package/node_modules/@jsonstudio/llms/dist/router/virtual-router/token-counter.js +105 -0
  211. package/node_modules/@jsonstudio/llms/dist/router/virtual-router/types.d.ts +8 -0
  212. package/node_modules/@jsonstudio/llms/dist/router/virtual-router/types.js +2 -2
  213. package/node_modules/@jsonstudio/llms/dist/sse/sse-to-json/builders/response-builder.d.ts +2 -0
  214. package/node_modules/@jsonstudio/llms/dist/sse/sse-to-json/builders/response-builder.js +53 -11
  215. package/node_modules/@jsonstudio/llms/dist/test-output/virtual-router/results.json +1 -0
  216. package/node_modules/@jsonstudio/llms/dist/test-output/virtual-router/summary.json +12 -0
  217. package/node_modules/@jsonstudio/llms/dist/tools/tool-registry.js +4 -3
  218. package/node_modules/@jsonstudio/llms/package.json +3 -3
  219. package/package.json +11 -9
  220. package/scripts/analyze-routing-classifier.mjs +166 -0
  221. package/scripts/analyze-routing-samples.mjs +216 -0
  222. package/scripts/analyze-thinking-keywords.mjs +17 -2
  223. package/scripts/build-core.mjs +8 -0
  224. package/scripts/classify-sample-tools.mjs +252 -0
  225. package/scripts/ensure-llmswitch-mode.mjs +95 -0
  226. package/scripts/gen-build-info.mjs +58 -4
  227. package/scripts/install-global.sh +1 -1
  228. package/scripts/install-release.sh +7 -0
  229. package/scripts/mock-provider/run-regressions.mjs +60 -14
  230. package/scripts/tests/apply-patch-loop.mjs +100 -9
  231. package/scripts/tests/golden-provider-cycle.mjs +12 -1
  232. package/scripts/tests/responses-provider-dry-run.mjs +15 -1
  233. package/scripts/tests/virtual-router-health.mjs +12 -5
  234. package/scripts/tools/capture-provider-goldens.mjs +75 -25
  235. package/scripts/tools/responses-golden-dry-run.mjs +17 -1
  236. package/scripts/tools/responses-provider-replay.mjs +17 -1
  237. package/scripts/tools/sync-ci-goldens.mjs +131 -0
  238. package/scripts/verification/samples/openai-chat-list-local-files.json +19 -796
  239. package/scripts/verify-e2e-toolcall.mjs +52 -0
  240. package/dist/providers/compat/config/index.d.ts +0 -1
  241. package/dist/providers/compat/config/index.js +0 -5
  242. package/dist/providers/compat/config/index.js.map +0 -1
  243. package/dist/providers/compat/iflow/index.d.ts +0 -27
  244. package/dist/providers/compat/iflow/index.js +0 -32
  245. package/dist/providers/compat/iflow/index.js.map +0 -1
  246. package/dist/providers/compat/lmstudio/index.d.ts +0 -4
  247. package/dist/providers/compat/lmstudio/index.js +0 -10
  248. package/dist/providers/compat/lmstudio/index.js.map +0 -1
  249. package/dist/providers/compat/passthrough/index.d.ts +0 -4
  250. package/dist/providers/compat/passthrough/index.js +0 -9
  251. package/dist/providers/compat/passthrough/index.js.map +0 -1
  252. package/dist/providers/compat/responses/index.d.ts +0 -1
  253. package/dist/providers/compat/responses/index.js +0 -8
  254. package/dist/providers/compat/responses/index.js.map +0 -1
  255. package/dist/providers/core/composite/compat/anthropic.d.ts +0 -3
  256. package/dist/providers/core/composite/compat/anthropic.js +0 -7
  257. package/dist/providers/core/composite/compat/anthropic.js.map +0 -1
  258. package/dist/providers/core/composite/compat/gemini.d.ts +0 -3
  259. package/dist/providers/core/composite/compat/gemini.js +0 -7
  260. package/dist/providers/core/composite/compat/gemini.js.map +0 -1
  261. package/dist/providers/core/composite/compat/openai-compat-aggregator.d.ts +0 -9
  262. package/dist/providers/core/composite/compat/openai-compat-aggregator.js +0 -135
  263. package/dist/providers/core/composite/compat/openai-compat-aggregator.js.map +0 -1
  264. package/dist/providers/core/composite/compat/responses.d.ts +0 -3
  265. package/dist/providers/core/composite/compat/responses.js +0 -91
  266. package/dist/providers/core/composite/compat/responses.js.map +0 -1
  267. package/dist/providers/core/composite/provider-composite.d.ts +0 -50
  268. package/dist/providers/core/composite/provider-composite.js +0 -235
  269. package/dist/providers/core/composite/provider-composite.js.map +0 -1
  270. package/scripts/tests/apply-patch-loop.mjs.bak +0 -363
@@ -73,7 +73,7 @@ export class RequestOpenAIToolsNormalizeFilter {
73
73
  delete dst.function.strict;
74
74
  }
75
75
  catch { /* ignore */ }
76
- // Switch schema for 'shell' at unified shaping point
76
+ // Switch schema for specific built-in tools at unified shaping point
77
77
  try {
78
78
  const name = String(dst.function.name || '').toLowerCase();
79
79
  if (name === 'shell') {
@@ -101,6 +101,25 @@ export class RequestOpenAIToolsNormalizeFilter {
101
101
  };
102
102
  }
103
103
  }
104
+ else if (name === 'apply_patch') {
105
+ dst.function.parameters = {
106
+ type: 'object',
107
+ properties: {
108
+ input: {
109
+ type: 'string',
110
+ description: 'Unified diff patch body between *** Begin Patch/*** End Patch.'
111
+ },
112
+ patch: {
113
+ type: 'string',
114
+ description: 'Alias of input for backwards compatibility.'
115
+ }
116
+ },
117
+ required: ['input'],
118
+ additionalProperties: false
119
+ };
120
+ dst.function.description =
121
+ 'Use apply_patch to edit files. Provide the diff via the input field (*** Begin Patch ... *** End Patch).';
122
+ }
104
123
  }
105
124
  catch { /* ignore */ }
106
125
  finalTools.push(dst);
@@ -63,6 +63,8 @@ function augmentApplyPatch(fn) {
63
63
  const guidance = [
64
64
  marker,
65
65
  'Edit files by applying a unified diff patch. Return ONLY the patch text with *** Begin Patch/*** End Patch blocks.',
66
+ 'Paths resolve relative to the active workspace root. Use forward-slash paths (e.g., packages/foo/file.ts) and switch to absolute paths only if you truly need to edit outside the workspace.',
67
+ '路径一律相对于当前工作区根目录解析;请写 packages/foo/... 这样的相对路径,跨工作区时再使用绝对路径。',
66
68
  'Example:',
67
69
  '*** Begin Patch',
68
70
  '*** Update File: path/to/file.ts',
@@ -181,7 +183,9 @@ export function augmentAnthropicTools(tools) {
181
183
  const marker = '[Codex ApplyPatch Guidance]';
182
184
  const guidance = [
183
185
  marker,
184
- 'Use unified diff patch with *** Begin Patch/End Patch. Return only the patch text.'
186
+ 'Use unified diff patch with *** Begin Patch/End Patch. Return only the patch text.',
187
+ 'All file paths must stay relative to the workspace root; never emit leading \'/\' or drive letters.',
188
+ '所有文件路径都必须相对当前工作区根目录,禁止输出以 / 或盘符开头的绝对路径。'
185
189
  ].join('\n');
186
190
  copy.description = appendOnce(desc, guidance, marker);
187
191
  }
@@ -216,7 +220,7 @@ export function buildSystemToolGuidance() {
216
220
  lines.push(bullet('function.arguments must be a single JSON string. / arguments 必须是单个 JSON 字符串。'));
217
221
  lines.push(bullet('shell: Place ALL intent into the command argv array only; do not invent extra keys. / shell 所有意图写入 command 数组,不要添加额外键名。'));
218
222
  lines.push(bullet('File writes are FORBIDDEN via shell (no redirection, no here-doc, no sed -i, no ed -s, no tee). Use apply_patch ONLY. / 通过 shell 写文件一律禁止(不得使用重定向、heredoc、sed -i、ed -s、tee);必须使用 apply_patch。'));
219
- lines.push(bullet('apply_patch: Provide a unified diff patch with *** Begin Patch/*** End Patch only. / 仅输出统一 diff 补丁。'));
223
+ lines.push(bullet('apply_patch: Provide a unified diff patch with *** Begin Patch/*** End Patch only, and keep file paths relative to the workspace (no leading / or drive letters). / 仅输出统一 diff 补丁,且文件路径必须是相对路径(禁止以 / 或盘符开头)。'));
220
224
  lines.push(bullet('apply_patch example / 示例:\n*** Begin Patch\n*** Update File: path/to/file.ts\n@@\n- old line\n+ new line\n*** End Patch'));
221
225
  lines.push(bullet('update_plan: Keep exactly one step in_progress; others pending/completed. / 仅一个 in_progress 步骤。'));
222
226
  lines.push(bullet('view_image: Path must be an image file (.png .jpg .jpeg .gif .webp .bmp .svg). / 仅图片路径。'));
@@ -1,6 +1,6 @@
1
1
  import { VirtualRouterError, VirtualRouterErrorCode } from './types.js';
2
2
  const DEFAULT_CLASSIFIER = {
3
- longContextThresholdTokens: 60000,
3
+ longContextThresholdTokens: 180000,
4
4
  thinkingKeywords: ['think step', 'analysis', 'reasoning', '仔细分析', '深度思考'],
5
5
  codingKeywords: ['apply_patch', 'write_file', 'create_file', 'shell', '修改文件', '写入文件'],
6
6
  backgroundKeywords: ['background', 'context dump', '上下文'],
@@ -208,7 +208,7 @@ function normalizeProvider(providerId, raw) {
208
208
  ? provider.baseUrl.trim()
209
209
  : '';
210
210
  const headers = normalizeHeaders(provider.headers);
211
- const compatibilityProfile = resolveCompatibilityProfile(provider);
211
+ const compatibilityProfile = resolveCompatibilityProfile(providerId, provider);
212
212
  const responsesConfig = normalizeResponsesConfig(provider);
213
213
  const processMode = normalizeProcessMode(provider.process);
214
214
  return {
@@ -233,12 +233,21 @@ function normalizeResponsesConfig(provider) {
233
233
  }
234
234
  return undefined;
235
235
  }
236
- function resolveCompatibilityProfile(provider) {
237
- const compat = provider.compat;
238
- if (typeof compat === 'string' && compat.trim().length > 0) {
239
- return compat.trim();
236
+ function resolveCompatibilityProfile(providerId, provider) {
237
+ if (typeof provider.compatibilityProfile === 'string' && provider.compatibilityProfile.trim()) {
238
+ return provider.compatibilityProfile.trim();
240
239
  }
241
- return 'default';
240
+ const legacyFields = [];
241
+ if (typeof provider.compat === 'string') {
242
+ legacyFields.push('compat');
243
+ }
244
+ if (typeof provider.compatibility_profile === 'string') {
245
+ legacyFields.push('compatibility_profile');
246
+ }
247
+ if (legacyFields.length > 0) {
248
+ throw new VirtualRouterError(`Provider "${providerId}" uses legacy compatibility field(s): ${legacyFields.join(', ')}. Rename to "compatibilityProfile".`, VirtualRouterErrorCode.CONFIG_ERROR);
249
+ }
250
+ return 'compat:passthrough';
242
251
  }
243
252
  function normalizeProcessMode(value) {
244
253
  if (typeof value !== 'string') {
@@ -1,5 +1,5 @@
1
1
  import { DEFAULT_ROUTE, ROUTE_PRIORITY } from './types.js';
2
- const DEFAULT_LONG_CONTEXT_THRESHOLD = 60000;
2
+ const DEFAULT_LONG_CONTEXT_THRESHOLD = 180000;
3
3
  export class RoutingClassifier {
4
4
  config;
5
5
  constructor(config) {
@@ -10,55 +10,58 @@ export class RoutingClassifier {
10
10
  };
11
11
  }
12
12
  classify(features) {
13
- const evaluations = [
14
- {
15
- route: 'vision',
13
+ const lastToolCategory = features.lastAssistantToolCategory;
14
+ const reachedLongContext = features.estimatedTokens >= (this.config.longContextThresholdTokens ?? DEFAULT_LONG_CONTEXT_THRESHOLD);
15
+ const thinkingKeywordHit = features.hasThinkingKeyword ||
16
+ containsKeywords(features.userTextSample, this.config.thinkingKeywords ?? []);
17
+ const codingContinuation = lastToolCategory === 'write';
18
+ const thinkingContinuation = lastToolCategory === 'read';
19
+ const searchContinuation = lastToolCategory === 'search';
20
+ const toolsContinuation = lastToolCategory === 'other';
21
+ const evaluationMap = {
22
+ vision: {
16
23
  triggered: features.hasVisionTool && features.hasImageAttachment,
17
24
  reason: 'vision:requires-tool+image'
18
25
  },
19
- {
20
- route: 'websearch',
21
- triggered: features.hasWebTool,
22
- reason: 'websearch:web-tools-detected'
26
+ longcontext: {
27
+ triggered: reachedLongContext,
28
+ reason: 'longcontext:token-threshold'
23
29
  },
24
- {
25
- route: 'coding',
26
- triggered: features.hasCodingTool,
27
- reason: 'coding:coding-tools-detected'
30
+ websearch: {
31
+ triggered: features.hasWebTool || searchContinuation,
32
+ reason: searchContinuation ? 'websearch:last-tool-search' : 'websearch:web-tools-detected'
28
33
  },
29
- {
30
- route: 'tools',
31
- triggered: features.hasTools || features.hasToolCallResponses,
32
- reason: 'tools:tool-request-detected'
34
+ coding: {
35
+ triggered: codingContinuation,
36
+ reason: 'coding:last-tool-write'
33
37
  },
34
- {
35
- route: 'longcontext',
36
- triggered: features.estimatedTokens >= (this.config.longContextThresholdTokens ?? DEFAULT_LONG_CONTEXT_THRESHOLD),
37
- reason: 'longcontext:token-threshold'
38
+ thinking: {
39
+ triggered: thinkingContinuation || thinkingKeywordHit,
40
+ reason: thinkingContinuation ? 'thinking:last-tool-read' : 'thinking:keywords'
38
41
  },
39
- {
40
- route: 'thinking',
41
- triggered: features.hasThinkingKeyword ||
42
- containsKeywords(features.userTextSample, this.config.thinkingKeywords ?? []),
43
- reason: 'thinking:keywords'
42
+ tools: {
43
+ triggered: toolsContinuation || features.hasTools || features.hasToolCallResponses,
44
+ reason: toolsContinuation ? 'tools:last-tool-other' : 'tools:tool-request-detected'
44
45
  },
45
- {
46
- route: 'background',
46
+ background: {
47
47
  triggered: containsKeywords(features.userTextSample, this.config.backgroundKeywords ?? []),
48
48
  reason: 'background:keywords'
49
49
  }
50
- ];
51
- const triggeredEvaluations = evaluations.filter((evaluation) => evaluation.triggered);
52
- const orderedRoutes = this.orderRoutes(triggeredEvaluations.map((entry) => entry.route));
53
- const chosenRoute = orderedRoutes.length ? orderedRoutes[0] : DEFAULT_ROUTE;
54
- const chosenReason = triggeredEvaluations.find((entry) => entry.route === chosenRoute)?.reason || 'fallback:default';
55
- const candidates = this.ensureDefaultCandidate(orderedRoutes);
56
- return this.buildResult(chosenRoute, chosenReason, evaluations, candidates);
50
+ };
51
+ for (const routeName of ROUTE_PRIORITY) {
52
+ const evaluation = evaluationMap[routeName];
53
+ if (evaluation && evaluation.triggered) {
54
+ const candidates = this.ensureDefaultCandidate([routeName]);
55
+ return this.buildResult(routeName, evaluation.reason, evaluationMap, candidates);
56
+ }
57
+ }
58
+ const candidates = this.ensureDefaultCandidate([DEFAULT_ROUTE]);
59
+ return this.buildResult(DEFAULT_ROUTE, 'fallback:default', evaluationMap, candidates);
57
60
  }
58
61
  buildResult(routeName, chosenReason, evaluations, candidates) {
59
- const diagnostics = evaluations
60
- .filter((evaluation) => evaluation.triggered)
61
- .map((evaluation) => evaluation.reason);
62
+ const diagnostics = Object.entries(evaluations)
63
+ .filter(([_, evaluation]) => evaluation.triggered)
64
+ .map(([_, evaluation]) => evaluation.reason);
62
65
  const reasoningParts = [chosenReason, ...diagnostics.filter((reason) => reason !== chosenReason)];
63
66
  return {
64
67
  routeName,
@@ -0,0 +1 @@
1
+ export declare const DEFAULT_THINKING_KEYWORDS: string[];
@@ -0,0 +1,13 @@
1
+ export const DEFAULT_THINKING_KEYWORDS = [
2
+ '思考',
3
+ '深度思考',
4
+ '分析',
5
+ '推理',
6
+ '思路',
7
+ '一步一步',
8
+ '慢慢',
9
+ 'think',
10
+ 'thinking',
11
+ 'step by step',
12
+ 'reason'
13
+ ];
@@ -0,0 +1,39 @@
1
+ import { type RoutingDecision, type RoutingDiagnostics, type RouterMetadataInput, type VirtualRouterConfig, type TargetMetadata, type ProviderFailureEvent, type ProviderErrorEvent } from './types.js';
2
+ import type { ProcessedRequest, StandardizedRequest } from '../../conversion/hub/types/standardized.js';
3
+ export declare class VirtualRouterEngine {
4
+ private routing;
5
+ private readonly providerRegistry;
6
+ private readonly healthManager;
7
+ private loadBalancer;
8
+ private classifier;
9
+ private routeStats;
10
+ private readonly debug;
11
+ private healthConfig;
12
+ initialize(config: VirtualRouterConfig): void;
13
+ route(request: StandardizedRequest | ProcessedRequest, metadata: RouterMetadataInput): {
14
+ target: TargetMetadata;
15
+ decision: RoutingDecision;
16
+ diagnostics: RoutingDiagnostics;
17
+ };
18
+ handleProviderFailure(event: ProviderFailureEvent): void;
19
+ handleProviderError(event: ProviderErrorEvent): void;
20
+ getStatus(): {
21
+ routes: Record<string, {
22
+ providers: string[];
23
+ hits: number;
24
+ lastUsedProvider?: string;
25
+ }>;
26
+ health: import("./types.js").ProviderHealthState[];
27
+ };
28
+ private validateConfig;
29
+ private selectProvider;
30
+ private incrementRouteStat;
31
+ private providerHealthConfig;
32
+ private resolveStickyKey;
33
+ private mapProviderError;
34
+ private deriveReason;
35
+ private buildRouteCandidates;
36
+ private sortByPriority;
37
+ private routeWeight;
38
+ private buildHitReason;
39
+ }
@@ -11,11 +11,14 @@ export class VirtualRouterEngine {
11
11
  loadBalancer = new RouteLoadBalancer();
12
12
  classifier = new RoutingClassifier({});
13
13
  routeStats = new Map();
14
+ debug = console; // thin hook; host may monkey-patch for colored logging
15
+ healthConfig = null;
14
16
  initialize(config) {
15
17
  this.validateConfig(config);
16
18
  this.routing = config.routing;
17
19
  this.providerRegistry.load(config.providers);
18
20
  this.healthManager.configure(config.health);
21
+ this.healthConfig = config.health ?? null;
19
22
  this.healthManager.registerProviders(Object.keys(config.providers));
20
23
  this.loadBalancer = new RouteLoadBalancer(config.loadBalancing);
21
24
  this.classifier = new RoutingClassifier(config.classifier);
@@ -32,6 +35,8 @@ export class VirtualRouterEngine {
32
35
  const target = this.providerRegistry.buildTarget(selection.providerKey);
33
36
  this.healthManager.recordSuccess(selection.providerKey);
34
37
  this.incrementRouteStat(selection.routeUsed, selection.providerKey);
38
+ const hitReason = this.buildHitReason(selection.routeUsed, classification, features);
39
+ this.debug?.log?.('[virtual-router-hit]', selection.routeUsed, selection.providerKey, target.modelId || '', hitReason ? `reason=${hitReason}` : '');
35
40
  const didFallback = selection.routeUsed !== routeName || classification.fallback;
36
41
  return {
37
42
  target,
@@ -117,7 +122,7 @@ export class VirtualRouterEngine {
117
122
  }
118
123
  selectProvider(requestedRoute, metadata, classification) {
119
124
  const candidates = this.buildRouteCandidates(requestedRoute, classification.candidates);
120
- const stickyKey = metadata.requestId;
125
+ const stickyKey = this.resolveStickyKey(metadata);
121
126
  const attempted = [];
122
127
  for (const routeName of candidates) {
123
128
  const pool = this.routing[routeName];
@@ -147,7 +152,20 @@ export class VirtualRouterEngine {
147
152
  stats.hits += 1;
148
153
  stats.lastProvider = providerKey;
149
154
  }
155
+ providerHealthConfig() {
156
+ return this.healthManager.getConfig();
157
+ }
158
+ resolveStickyKey(metadata) {
159
+ const resume = metadata.responsesResume;
160
+ if (resume && typeof resume.previousRequestId === 'string' && resume.previousRequestId.trim()) {
161
+ return resume.previousRequestId.trim();
162
+ }
163
+ return metadata.requestId;
164
+ }
150
165
  mapProviderError(event) {
166
+ // NOTE: mapProviderError is the only place where VirtualRouter translates providerErrorCenter
167
+ // events into health signals. Classification is intentionally coarse; upstream providers
168
+ // are expected to set event.recoverable explicitly when they know an error is safe to retry.
151
169
  if (!event || !event.runtime) {
152
170
  return null;
153
171
  }
@@ -162,19 +180,23 @@ export class VirtualRouterEngine {
162
180
  const code = event.code?.toUpperCase() ?? 'ERR_UNKNOWN';
163
181
  const stage = event.stage?.toLowerCase() ?? 'unknown';
164
182
  const recoverable = event.recoverable === true;
183
+ // 默认策略:只有显式可恢复的错误才视为非致命;其余一律按致命处理。
184
+ // 注意:provider 层已经对 429 做了「连续 4 次升级为不可恢复」的判断,这里不再把所有 429 强行当作可恢复。
165
185
  let fatal = !recoverable;
166
186
  let reason = this.deriveReason(code, stage, statusCode);
167
187
  let cooldownOverrideMs;
168
- if (statusCode === 429 || code.includes('429') || recoverable) {
169
- fatal = false;
170
- cooldownOverrideMs = Math.max(30_000, this.providerHealthConfig().cooldownMs);
171
- reason = 'rate_limit';
172
- }
173
- else if (statusCode === 401 || statusCode === 403 || code.includes('AUTH')) {
188
+ // 401 / 402 / 500 / 524 以及所有未被标记为可恢复的错误一律视为不可恢复
189
+ if (statusCode === 401 || statusCode === 402 || statusCode === 403 || code.includes('AUTH')) {
174
190
  fatal = true;
175
191
  cooldownOverrideMs = Math.max(10 * 60_000, this.providerHealthConfig().fatalCooldownMs ?? 10 * 60_000);
176
192
  reason = 'auth';
177
193
  }
194
+ else if (statusCode === 429 && !recoverable) {
195
+ // 连续 429 已在 provider 层被升级为不可恢复:这里按致命限流处理(长冷却,等同熔断)
196
+ fatal = true;
197
+ cooldownOverrideMs = Math.max(10 * 60_000, this.providerHealthConfig().fatalCooldownMs ?? 10 * 60_000);
198
+ reason = 'rate_limit';
199
+ }
178
200
  else if (statusCode && statusCode >= 500) {
179
201
  fatal = true;
180
202
  cooldownOverrideMs = Math.max(5 * 60_000, this.providerHealthConfig().fatalCooldownMs ?? 5 * 60_000);
@@ -193,7 +215,8 @@ export class VirtualRouterEngine {
193
215
  statusCode,
194
216
  errorCode: code,
195
217
  retryable: recoverable,
196
- affectsHealth: true,
218
+ // 是否影响健康由 provider 层决定;这里仅在 event.affectsHealth !== false 时才计入健康状态
219
+ affectsHealth: event.affectsHealth !== false,
197
220
  cooldownOverrideMs,
198
221
  metadata: {
199
222
  ...event.runtime,
@@ -221,9 +244,6 @@ export class VirtualRouterEngine {
221
244
  return 'client_error';
222
245
  return 'unknown';
223
246
  }
224
- providerHealthConfig() {
225
- return this.healthManager.getConfig();
226
- }
227
247
  buildRouteCandidates(requestedRoute, classificationCandidates) {
228
248
  const normalized = requestedRoute || DEFAULT_ROUTE;
229
249
  const baseList = classificationCandidates && classificationCandidates.length
@@ -257,4 +277,25 @@ export class VirtualRouterEngine {
257
277
  const idx = ROUTE_PRIORITY.indexOf(routeName);
258
278
  return idx >= 0 ? idx : ROUTE_PRIORITY.length;
259
279
  }
280
+ buildHitReason(routeUsed, classification, features) {
281
+ const reasoning = classification.reasoning || '';
282
+ const primary = reasoning.split('|')[0] || '';
283
+ const lastToolName = features.lastAssistantToolName;
284
+ if (routeUsed === 'tools') {
285
+ if (lastToolName) {
286
+ return primary ? `${primary}(${lastToolName})` : `tools(${lastToolName})`;
287
+ }
288
+ return primary || 'tools';
289
+ }
290
+ if (routeUsed === 'thinking') {
291
+ return primary || 'thinking';
292
+ }
293
+ if (routeUsed === DEFAULT_ROUTE && classification.fallback) {
294
+ return primary || 'fallback:default';
295
+ }
296
+ if (primary) {
297
+ return primary;
298
+ }
299
+ return routeUsed ? `route:${routeUsed}` : 'route:unknown';
300
+ }
260
301
  }