@jsonstudio/rcc 0.90.814 → 0.90.876

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 (221) hide show
  1. package/README.md +8 -0
  2. package/configsamples/provider-default/ali-coding-plan/config.v2.json +76 -0
  3. package/configsamples/provider-default/antigravity/config.v2.json +142 -0
  4. package/configsamples/provider-default/ark-coding-plan/config.v2.json +64 -0
  5. package/configsamples/provider-default/crs/config.v2.json +54 -0
  6. package/configsamples/provider-default/deepseek-web/config.v2.json +56 -0
  7. package/configsamples/provider-default/gemini/config.v2.json +43 -0
  8. package/configsamples/provider-default/gemini-cli/config.v2.json +45 -0
  9. package/configsamples/provider-default/gemini-native/config.v2.json +208 -0
  10. package/configsamples/provider-default/glm/config.v2.json +33 -0
  11. package/configsamples/provider-default/glm-anthropic/config.v2.json +29 -0
  12. package/configsamples/provider-default/kimi/config.v2.json +25 -0
  13. package/configsamples/provider-default/lmstudio/config.v2.json +79 -0
  14. package/configsamples/provider-default/lmstudio-proxy/config.v2.json +78 -0
  15. package/configsamples/provider-default/manifest.json +31 -0
  16. package/configsamples/provider-default/meituan/config.v2.json +20 -0
  17. package/configsamples/provider-default/mimo/config.v2.json +26 -0
  18. package/configsamples/provider-default/modelscope/config.v2.json +81 -0
  19. package/configsamples/provider-default/my-openai/config.v2.json +20 -0
  20. package/configsamples/provider-default/nvidia/config.v2.json +32 -0
  21. package/configsamples/provider-default/opencode-zen-free/config.v2.json +56 -0
  22. package/configsamples/provider-default/openrouter/config.v2.json +210 -0
  23. package/configsamples/provider-default/qwen/config.v2.json +38 -0
  24. package/configsamples/provider-default/qwenchat/config.v2.json +53 -0
  25. package/configsamples/provider-default/tab/config.v2.json +26 -0
  26. package/configsamples/provider-default/tabglm/config.v2.json +77 -0
  27. package/dist/build-info.js +2 -2
  28. package/dist/cli/commands/config.d.ts +5 -0
  29. package/dist/cli/commands/config.js +369 -1
  30. package/dist/cli/commands/config.js.map +1 -1
  31. package/dist/cli/commands/examples.js +3 -0
  32. package/dist/cli/commands/examples.js.map +1 -1
  33. package/dist/cli/commands/init.js +25 -1
  34. package/dist/cli/commands/init.js.map +1 -1
  35. package/dist/cli/commands/launcher-kernel.js +122 -46
  36. package/dist/cli/commands/launcher-kernel.js.map +1 -1
  37. package/dist/cli/commands/start.js +60 -3
  38. package/dist/cli/commands/start.js.map +1 -1
  39. package/dist/cli/config/bundled-provider-pack.d.ts +20 -0
  40. package/dist/cli/config/bundled-provider-pack.js +146 -0
  41. package/dist/cli/config/bundled-provider-pack.js.map +1 -0
  42. package/dist/cli/register/status-config-commands.d.ts +2 -0
  43. package/dist/cli/register/status-config-commands.js.map +1 -1
  44. package/dist/cli.js +81 -28
  45. package/dist/cli.js.map +1 -1
  46. package/dist/debug/snapshot-store.js +2 -1
  47. package/dist/debug/snapshot-store.js.map +1 -1
  48. package/dist/index.js +23 -14
  49. package/dist/index.js.map +1 -1
  50. package/dist/manager/modules/quota/provider-quota-daemon.model-backoff.js +1 -1
  51. package/dist/manager/modules/quota/provider-quota-daemon.model-backoff.js.map +1 -1
  52. package/dist/manager/quota/provider-quota-center.js +1 -1
  53. package/dist/manager/quota/provider-quota-center.js.map +1 -1
  54. package/dist/manager/storage/file-store.js +10 -0
  55. package/dist/manager/storage/file-store.js.map +1 -1
  56. package/dist/modules/llmswitch/bridge/snapshot-recorder-runtime.js +18 -1
  57. package/dist/modules/llmswitch/bridge/snapshot-recorder-runtime.js.map +1 -1
  58. package/dist/modules/llmswitch/bridge/snapshot-recorder.js +132 -51
  59. package/dist/modules/llmswitch/bridge/snapshot-recorder.js.map +1 -1
  60. package/dist/provider-sdk/provider-runtime-inference.js +2 -2
  61. package/dist/provider-sdk/provider-runtime-inference.js.map +1 -1
  62. package/dist/providers/auth/deepseek-account-token-acquirer.js +32 -3
  63. package/dist/providers/auth/deepseek-account-token-acquirer.js.map +1 -1
  64. package/dist/providers/core/api/provider-types.d.ts +11 -0
  65. package/dist/providers/core/config/service-profiles.js +1 -1
  66. package/dist/providers/core/runtime/deepseek-http-provider.d.ts +2 -0
  67. package/dist/providers/core/runtime/deepseek-http-provider.js +31 -1
  68. package/dist/providers/core/runtime/deepseek-http-provider.js.map +1 -1
  69. package/dist/providers/core/runtime/qwenchat-http-provider-helpers.d.ts +3 -2
  70. package/dist/providers/core/runtime/qwenchat-http-provider-helpers.js +513 -96
  71. package/dist/providers/core/runtime/qwenchat-http-provider-helpers.js.map +1 -1
  72. package/dist/providers/core/runtime/standard-tool-text-harvest.d.ts +8 -0
  73. package/dist/providers/core/runtime/standard-tool-text-harvest.js +24 -0
  74. package/dist/providers/core/runtime/standard-tool-text-harvest.js.map +1 -0
  75. package/dist/providers/core/runtime/standard-tool-text-request-transform.d.ts +4 -1
  76. package/dist/providers/core/runtime/standard-tool-text-request-transform.js +129 -3
  77. package/dist/providers/core/runtime/standard-tool-text-request-transform.js.map +1 -1
  78. package/dist/providers/core/utils/snapshot-writer.js +5 -2
  79. package/dist/providers/core/utils/snapshot-writer.js.map +1 -1
  80. package/dist/providers/profile/provider-profile-loader.js +52 -1
  81. package/dist/providers/profile/provider-profile-loader.js.map +1 -1
  82. package/dist/providers/profile/provider-profile.d.ts +3 -0
  83. package/dist/server/handlers/handler-response-utils.js +1 -0
  84. package/dist/server/handlers/handler-response-utils.js.map +1 -1
  85. package/dist/server/handlers/images-handler.d.ts +9 -0
  86. package/dist/server/handlers/images-handler.js +258 -0
  87. package/dist/server/handlers/images-handler.js.map +1 -0
  88. package/dist/server/handlers/types.d.ts +7 -0
  89. package/dist/server/runtime/http-server/antigravity-startup-tasks.d.ts +3 -0
  90. package/dist/server/runtime/http-server/antigravity-startup-tasks.js +16 -0
  91. package/dist/server/runtime/http-server/antigravity-startup-tasks.js.map +1 -1
  92. package/dist/server/runtime/http-server/daemon-admin/auth-handler.js +3 -18
  93. package/dist/server/runtime/http-server/daemon-admin/auth-handler.js.map +1 -1
  94. package/dist/server/runtime/http-server/daemon-admin/providers-handler-utils.d.ts +7 -0
  95. package/dist/server/runtime/http-server/daemon-admin/providers-handler-utils.js +17 -0
  96. package/dist/server/runtime/http-server/daemon-admin/providers-handler-utils.js.map +1 -1
  97. package/dist/server/runtime/http-server/daemon-admin/providers-handler.js +50 -17
  98. package/dist/server/runtime/http-server/daemon-admin/providers-handler.js.map +1 -1
  99. package/dist/server/runtime/http-server/daemon-admin-routes.js +32 -2
  100. package/dist/server/runtime/http-server/daemon-admin-routes.js.map +1 -1
  101. package/dist/server/runtime/http-server/executor/provider-response-converter.js +42 -13
  102. package/dist/server/runtime/http-server/executor/provider-response-converter.js.map +1 -1
  103. package/dist/server/runtime/http-server/executor/provider-response-utils.js +41 -3
  104. package/dist/server/runtime/http-server/executor/provider-response-utils.js.map +1 -1
  105. package/dist/server/runtime/http-server/executor/usage-aggregator.js +7 -7
  106. package/dist/server/runtime/http-server/executor/usage-aggregator.js.map +1 -1
  107. package/dist/server/runtime/http-server/executor/usage-logger.d.ts +9 -0
  108. package/dist/server/runtime/http-server/executor/usage-logger.js +35 -2
  109. package/dist/server/runtime/http-server/executor/usage-logger.js.map +1 -1
  110. package/dist/server/runtime/http-server/executor-metadata.js +12 -4
  111. package/dist/server/runtime/http-server/executor-metadata.js.map +1 -1
  112. package/dist/server/runtime/http-server/executor-pipeline.js +24 -15
  113. package/dist/server/runtime/http-server/executor-pipeline.js.map +1 -1
  114. package/dist/server/runtime/http-server/executor-provider.d.ts +6 -1
  115. package/dist/server/runtime/http-server/executor-provider.js +137 -5
  116. package/dist/server/runtime/http-server/executor-provider.js.map +1 -1
  117. package/dist/server/runtime/http-server/http-server-bootstrap.js +6 -0
  118. package/dist/server/runtime/http-server/http-server-bootstrap.js.map +1 -1
  119. package/dist/server/runtime/http-server/http-server-lifecycle.js +23 -15
  120. package/dist/server/runtime/http-server/http-server-lifecycle.js.map +1 -1
  121. package/dist/server/runtime/http-server/http-server-runtime-providers.js +14 -4
  122. package/dist/server/runtime/http-server/http-server-runtime-providers.js.map +1 -1
  123. package/dist/server/runtime/http-server/http-server-runtime-setup.js +83 -1
  124. package/dist/server/runtime/http-server/http-server-runtime-setup.js.map +1 -1
  125. package/dist/server/runtime/http-server/hub-shadow-compare.js +2 -41
  126. package/dist/server/runtime/http-server/hub-shadow-compare.js.map +1 -1
  127. package/dist/server/runtime/http-server/provider-routing-scope.d.ts +9 -0
  128. package/dist/server/runtime/http-server/provider-routing-scope.js +20 -0
  129. package/dist/server/runtime/http-server/provider-routing-scope.js.map +1 -0
  130. package/dist/server/runtime/http-server/provider-traffic-governor.d.ts +67 -0
  131. package/dist/server/runtime/http-server/provider-traffic-governor.js +467 -0
  132. package/dist/server/runtime/http-server/provider-traffic-governor.js.map +1 -0
  133. package/dist/server/runtime/http-server/request-executor.d.ts +8 -0
  134. package/dist/server/runtime/http-server/request-executor.js +446 -21
  135. package/dist/server/runtime/http-server/request-executor.js.map +1 -1
  136. package/dist/server/runtime/http-server/routes.js +13 -0
  137. package/dist/server/runtime/http-server/routes.js.map +1 -1
  138. package/dist/server/runtime/http-server/session-client-registry.js +30 -4
  139. package/dist/server/runtime/http-server/session-client-registry.js.map +1 -1
  140. package/dist/server/runtime/http-server/session-client-route-utils.d.ts +7 -0
  141. package/dist/server/runtime/http-server/session-client-route-utils.js +38 -0
  142. package/dist/server/runtime/http-server/session-client-route-utils.js.map +1 -1
  143. package/dist/server/runtime/http-server/session-client-routes.js +12 -2
  144. package/dist/server/runtime/http-server/session-client-routes.js.map +1 -1
  145. package/dist/server/utils/request-id-manager.js +42 -5
  146. package/dist/server/utils/request-id-manager.js.map +1 -1
  147. package/dist/server/utils/stage-logger.d.ts +1 -0
  148. package/dist/server/utils/stage-logger.js +27 -0
  149. package/dist/server/utils/stage-logger.js.map +1 -1
  150. package/dist/utils/errorsamples.js +3 -1
  151. package/dist/utils/errorsamples.js.map +1 -1
  152. package/dist/utils/sensitive-redaction.d.ts +1 -0
  153. package/dist/utils/sensitive-redaction.js +122 -0
  154. package/dist/utils/sensitive-redaction.js.map +1 -0
  155. package/dist/utils/snapshot-writer.js +162 -2
  156. package/dist/utils/snapshot-writer.js.map +1 -1
  157. package/docs/INSTALLATION_AND_QUICKSTART.md +14 -1
  158. package/docs/PORTS.md +12 -0
  159. package/docs/lmstudio-tool-calling.md +25 -0
  160. package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/qwenchat-web-request.d.ts +3 -0
  161. package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/qwenchat-web-request.js +62 -0
  162. package/node_modules/@jsonstudio/llms/dist/conversion/compat/profiles/chat-qwenchat-web.json +47 -0
  163. package/node_modules/@jsonstudio/llms/dist/conversion/hub/node-support.js +5 -2
  164. package/node_modules/@jsonstudio/llms/dist/conversion/hub/operation-table/operation-table-runner.js +68 -7
  165. package/node_modules/@jsonstudio/llms/dist/conversion/hub/operation-table/semantic-mappers/anthropic-mapper-from-chat.js +138 -3
  166. package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/hub-pipeline-chat-process-request-utils.js +24 -0
  167. package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/hub-pipeline-execute-chat-process-entry.js +7 -1
  168. package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/hub-pipeline-execute-request-stage.js +7 -0
  169. package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/hub-pipeline-heavy-input-fastpath.d.ts +24 -0
  170. package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/hub-pipeline-heavy-input-fastpath.js +203 -0
  171. package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/hub-pipeline-route-and-outbound.js +17 -12
  172. package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/hub-stage-timing.d.ts +11 -0
  173. package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/hub-stage-timing.js +82 -1
  174. package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage2_semantic_map/index.js +47 -14
  175. package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage1_semantic_map/index.js +43 -0
  176. package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/stages/resp_outbound/resp_outbound_stage1_client_remap/client-remap-protocol-switch.js +222 -19
  177. package/node_modules/@jsonstudio/llms/dist/conversion/hub/policy/policy-engine.js +2 -2
  178. package/node_modules/@jsonstudio/llms/dist/conversion/hub/process/chat-process-pending-tool-sync.js +24 -7
  179. package/node_modules/@jsonstudio/llms/dist/conversion/hub/response/provider-response.js +90 -1
  180. package/node_modules/@jsonstudio/llms/dist/conversion/hub/snapshot-recorder.d.ts +1 -0
  181. package/node_modules/@jsonstudio/llms/dist/conversion/hub/snapshot-recorder.js +252 -1
  182. package/node_modules/@jsonstudio/llms/dist/conversion/responses/responses-openai-bridge/utils.js +5 -3
  183. package/node_modules/@jsonstudio/llms/dist/conversion/responses/responses-openai-bridge.js +44 -4
  184. package/node_modules/@jsonstudio/llms/dist/conversion/shared/anthropic-message-utils-openai-request.d.ts +3 -1
  185. package/node_modules/@jsonstudio/llms/dist/conversion/shared/anthropic-message-utils-openai-request.js +20 -23
  186. package/node_modules/@jsonstudio/llms/dist/conversion/shared/tool-governor.js +68 -30
  187. package/node_modules/@jsonstudio/llms/dist/conversion/snapshot-utils.js +194 -10
  188. package/node_modules/@jsonstudio/llms/dist/native/router_hotpath_napi.node +0 -0
  189. package/node_modules/@jsonstudio/llms/dist/quota/quota-state.js +2 -2
  190. package/node_modules/@jsonstudio/llms/dist/router/virtual-router/engine/routing-state/store.js +35 -2
  191. package/node_modules/@jsonstudio/llms/dist/router/virtual-router/engine.js +9 -9
  192. package/node_modules/@jsonstudio/llms/dist/router/virtual-router/sticky-session-store.js +104 -18
  193. package/node_modules/@jsonstudio/llms/dist/servertool/engine.js +79 -32
  194. package/node_modules/@jsonstudio/llms/dist/servertool/handlers/vision.js +49 -0
  195. package/node_modules/@jsonstudio/llms/dist/servertool/pending-session.js +48 -2
  196. package/node_modules/@jsonstudio/llms/dist/servertool/server-side-tools.js +14 -1
  197. package/node_modules/@jsonstudio/llms/dist/servertool/types.d.ts +1 -0
  198. package/node_modules/@jsonstudio/llms/package.json +1 -1
  199. package/node_modules/ajv/dist/compile/jtd/serialize.js +9 -2
  200. package/node_modules/ajv/dist/compile/jtd/serialize.js.map +1 -1
  201. package/node_modules/ajv/dist/core.d.ts +1 -0
  202. package/node_modules/ajv/dist/core.js.map +1 -1
  203. package/node_modules/ajv/dist/vocabularies/validation/pattern.js +13 -4
  204. package/node_modules/ajv/dist/vocabularies/validation/pattern.js.map +1 -1
  205. package/node_modules/ajv/lib/compile/jtd/serialize.ts +13 -2
  206. package/node_modules/ajv/lib/core.ts +1 -0
  207. package/node_modules/ajv/lib/vocabularies/validation/pattern.ts +15 -4
  208. package/node_modules/ajv/package.json +2 -1
  209. package/package.json +15 -10
  210. package/scripts/ci/repo-sanity.mjs +23 -2
  211. package/scripts/ci/secrets-check.mjs +48 -0
  212. package/scripts/ci/silent-failure-audit.mjs +192 -0
  213. package/scripts/mock-provider/run-regressions.mjs +1 -0
  214. package/scripts/monitor/memory-guard.mjs +207 -0
  215. package/scripts/pack-mode.mjs +32 -36
  216. package/scripts/publish-rcc.mjs +38 -60
  217. package/scripts/tests/apply-patch-loop.mjs +1 -0
  218. package/scripts/tests/blackbox-rcc-vs-routecodex-antigravity.mjs +2 -0
  219. package/scripts/tools-dev/responses-debug-client/src/index.ts +8 -3
  220. package/scripts/verify-e2e-toolcall.mjs +1 -0
  221. package/scripts/verify-install-e2e.mjs +2 -1
@@ -13,6 +13,11 @@ import { resolveRccPath } from '../../runtime/user-data-paths.js';
13
13
  function isObject(v) { return !!v && typeof v === 'object' && !Array.isArray(v); }
14
14
  // Note: tool schema strict augmentation removed per alignment
15
15
  function enforceChatBudget(chat, _modelId) { return chat; }
16
+ function logToolGovernorNonBlocking(stage, error) {
17
+ const message = error instanceof Error ? error.message : String(error ?? 'unknown');
18
+ // eslint-disable-next-line no-console
19
+ console.warn(`[tool-governor][non-blocking] stage=${stage} error=${message}`);
20
+ }
16
21
  function tryWriteSnapshot(options, stage, data) {
17
22
  try {
18
23
  // 仅在 verbose 级别保存快照(环境变量)
@@ -37,7 +42,9 @@ function tryWriteSnapshot(options, stage, data) {
37
42
  const payload = JSON.stringify(data, null, 2);
38
43
  fs.writeFileSync(file, payload, 'utf-8');
39
44
  }
40
- catch { /* ignore snapshot errors */ }
45
+ catch (error) {
46
+ logToolGovernorNonBlocking(`snapshot_write:${stage}`, error);
47
+ }
41
48
  }
42
49
  /**
43
50
  * Process OpenAI Chat request (messages/tools) with unified 标准 governance.
@@ -74,7 +81,9 @@ export function processChatRequestTools(request, opts) {
74
81
  out.tools = augmentOpenAITools(tools);
75
82
  }
76
83
  }
77
- catch { /* best-effort: 保持原样 */ }
84
+ catch (error) {
85
+ logToolGovernorNonBlocking('request_minimal_tool_shape_repair', error);
86
+ }
78
87
  // 1) 移除工具 schema 严格化(与 统一标准,不在此处约束 tools 结构)
79
88
  // NOTE: system guidance injection removed by design (align with parameter-level strategy)
80
89
  try {
@@ -94,13 +103,16 @@ export function processChatRequestTools(request, opts) {
94
103
  }
95
104
  }
96
105
  }
97
- catch { /* ignore */ }
106
+ catch (error) {
107
+ logToolGovernorNonBlocking('request_tool_choice_policy', error);
108
+ }
98
109
  // 4) Enforce payload budget (context bytes) with minimal loss policy
99
110
  const modelId = typeof canonical?.model === 'string' ? String(canonical.model) : 'unknown';
100
111
  const budgeted = enforceChatBudget(canonical, modelId);
101
112
  return normalizeSpecialToolCallsOnRequest(budgeted);
102
113
  }
103
- catch {
114
+ catch (error) {
115
+ logToolGovernorNonBlocking('process_chat_request_tools', error);
104
116
  return out;
105
117
  }
106
118
  }
@@ -150,19 +162,20 @@ export function normalizeApplyPatchToolCallsOnResponse(chat) {
150
162
  // eslint-disable-next-line no-console
151
163
  console.error(`\x1b[31m[apply_patch][precheck][response] validation_failed reason=${reason}${snippet ? ` args=${snippet}` : ''}\x1b[0m`);
152
164
  }
153
- catch {
154
- // logging best-effort
165
+ catch (error) {
166
+ logToolGovernorNonBlocking('response_apply_patch_regression_capture', error);
155
167
  }
156
168
  }
157
169
  }
158
- catch {
159
- // best-effort per tool_call
170
+ catch (error) {
171
+ logToolGovernorNonBlocking('response_tool_call_normalize_item', error);
160
172
  }
161
173
  }
162
174
  }
163
175
  return out;
164
176
  }
165
- catch {
177
+ catch (error) {
178
+ logToolGovernorNonBlocking('normalize_apply_patch_tool_calls_on_response', error);
166
179
  return chat;
167
180
  }
168
181
  }
@@ -232,8 +245,8 @@ function normalizeSpecialToolCallsOnRequest(request) {
232
245
  // eslint-disable-next-line no-console
233
246
  console.error(`\x1b[31m[apply_patch][precheck][request] validation_failed reason=${reason}${snippet ? ` args=${snippet}` : ''}\x1b[0m`);
234
247
  }
235
- catch {
236
- // logging best-effort
248
+ catch (error) {
249
+ logToolGovernorNonBlocking('request_apply_patch_regression_capture', error);
237
250
  }
238
251
  }
239
252
  continue;
@@ -253,7 +266,8 @@ function normalizeSpecialToolCallsOnRequest(request) {
253
266
  try {
254
267
  parsed = JSON.parse(argsStr);
255
268
  }
256
- catch {
269
+ catch (error) {
270
+ logToolGovernorNonBlocking('request_exec_command_parse_json', error);
257
271
  parsed = parseLenient(argsStr);
258
272
  }
259
273
  if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
@@ -262,15 +276,16 @@ function normalizeSpecialToolCallsOnRequest(request) {
262
276
  try {
263
277
  fn.arguments = JSON.stringify(next ?? {});
264
278
  }
265
- catch {
279
+ catch (error) {
280
+ logToolGovernorNonBlocking('request_exec_command_json_stringify', error);
266
281
  fn.arguments = '{}';
267
282
  }
268
283
  }
269
284
  }
270
285
  }
271
286
  }
272
- catch {
273
- // best-effort per tool_call
287
+ catch (error) {
288
+ logToolGovernorNonBlocking('request_tool_call_normalize_item', error);
274
289
  }
275
290
  }
276
291
  }
@@ -279,7 +294,8 @@ function normalizeSpecialToolCallsOnRequest(request) {
279
294
  }
280
295
  return out;
281
296
  }
282
- catch {
297
+ catch (error) {
298
+ logToolGovernorNonBlocking('normalize_special_tool_calls_on_request', error);
283
299
  return request;
284
300
  }
285
301
  }
@@ -319,7 +335,8 @@ function enhanceResponseToolArguments(chat) {
319
335
  out = out.replace(/(?<!\\)\(/g, '\\(').replace(/(?<!\\)\)/g, '\\)');
320
336
  return out;
321
337
  }
322
- catch {
338
+ catch (error) {
339
+ logToolGovernorNonBlocking('enhance_response_tool_arguments_repair_find_meta', error);
323
340
  return s;
324
341
  }
325
342
  };
@@ -337,7 +354,8 @@ function enhanceResponseToolArguments(chat) {
337
354
  try {
338
355
  parsed = JSON.parse(repaired);
339
356
  }
340
- catch {
357
+ catch (error) {
358
+ logToolGovernorNonBlocking('enhance_response_tool_arguments_parse_json', error);
341
359
  parsed = parseLenient(repaired);
342
360
  }
343
361
  if (parsed && typeof parsed === 'object') {
@@ -367,7 +385,8 @@ function enhanceResponseToolArguments(chat) {
367
385
  try {
368
386
  finalStr = JSON.stringify(parsed);
369
387
  }
370
- catch {
388
+ catch (error) {
389
+ logToolGovernorNonBlocking('enhance_response_tool_arguments_json_stringify', error);
371
390
  finalStr = repaired;
372
391
  }
373
392
  }
@@ -384,23 +403,31 @@ function enhanceResponseToolArguments(chat) {
384
403
  if (fn)
385
404
  fn.arguments = finalStr;
386
405
  }
387
- catch { /* keep original */ }
406
+ catch (error) {
407
+ logToolGovernorNonBlocking('enhance_response_tool_arguments_item', error);
408
+ }
388
409
  }
389
410
  // Ensure finish_reason/tool_calls invariant if missing (idempotent with canonicalizer)
390
411
  try {
391
412
  if (!ch.finish_reason)
392
413
  ch.finish_reason = 'tool_calls';
393
414
  }
394
- catch { /* ignore */ }
415
+ catch (error) {
416
+ logToolGovernorNonBlocking('enhance_response_tool_arguments_finish_reason', error);
417
+ }
395
418
  try {
396
- if (msg && typeof msg === 'object' && Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0)
419
+ if (msg && typeof msg === 'object' && Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0) {
397
420
  msg.content = null;
421
+ }
422
+ }
423
+ catch (error) {
424
+ logToolGovernorNonBlocking('enhance_response_tool_arguments_message_content', error);
398
425
  }
399
- catch { /* ignore */ }
400
426
  }
401
427
  return out;
402
428
  }
403
- catch {
429
+ catch (error) {
430
+ logToolGovernorNonBlocking('enhance_response_tool_arguments', error);
404
431
  return chat;
405
432
  }
406
433
  }
@@ -412,7 +439,8 @@ export function processChatResponseTools(resp) {
412
439
  const withPatch = normalizeApplyPatchToolCallsOnResponse(canon);
413
440
  return enhanceResponseToolArguments(withPatch);
414
441
  }
415
- catch {
442
+ catch (error) {
443
+ logToolGovernorNonBlocking('process_chat_response_tools', error);
416
444
  return resp;
417
445
  }
418
446
  }
@@ -432,21 +460,27 @@ export function governTools(payload, ctx) {
432
460
  const opts = { snapshot: ctx?.snapshot || { enabled: true, endpoint: ep, requestId: ctx?.requestId } };
433
461
  tryWriteSnapshot(opts, 'response_before_canonicalize', payload);
434
462
  }
435
- catch { /* ignore */ }
463
+ catch (error) {
464
+ logToolGovernorNonBlocking('govern_tools_snapshot_before_canonicalize', error);
465
+ }
436
466
  let out = processChatResponseTools(payload);
437
467
  // 变更后快照:响应侧 canonicalize 之后
438
468
  try {
439
469
  const opts = { snapshot: ctx?.snapshot || { enabled: true, endpoint: ep, requestId: ctx?.requestId } };
440
470
  tryWriteSnapshot(opts, 'response_after_canonicalize', out);
441
471
  }
442
- catch { /* ignore */ }
472
+ catch (error) {
473
+ logToolGovernorNonBlocking('govern_tools_snapshot_after_canonicalize', error);
474
+ }
443
475
  if (ep === 'responses' && ctx?.stream !== true && ctx?.produceRequiredAction !== false) {
444
476
  // 变更前快照:构造 required_action 之前
445
477
  try {
446
478
  const opts = { snapshot: ctx?.snapshot || { enabled: true, endpoint: ep, requestId: ctx?.requestId } };
447
479
  tryWriteSnapshot(opts, 'response_before_required_action', out);
448
480
  }
449
- catch { /* ignore */ }
481
+ catch (error) {
482
+ logToolGovernorNonBlocking('govern_tools_snapshot_before_required_action', error);
483
+ }
450
484
  try {
451
485
  const { buildResponsesPayloadFromChat } = require('../responses/responses-openai-bridge.js');
452
486
  const res = buildResponsesPayloadFromChat(out, { requestId: ctx?.requestId });
@@ -454,10 +488,14 @@ export function governTools(payload, ctx) {
454
488
  const opts = { snapshot: ctx?.snapshot || { enabled: true, endpoint: ep, requestId: ctx?.requestId } };
455
489
  tryWriteSnapshot(opts, 'response_after_required_action', res);
456
490
  }
457
- catch { /* ignore */ }
491
+ catch (error) {
492
+ logToolGovernorNonBlocking('govern_tools_snapshot_after_required_action', error);
493
+ }
458
494
  return res;
459
495
  }
460
- catch { /* ignore mapping errors and return canonicalized chat */ }
496
+ catch (error) {
497
+ logToolGovernorNonBlocking('govern_tools_required_action_bridge', error);
498
+ }
461
499
  }
462
500
  return out;
463
501
  }
@@ -18,32 +18,216 @@ export async function recordSnapshot(options) {
18
18
  if (!shouldRecordSnapshots())
19
19
  return;
20
20
  const endpoint = options.endpoint || '/v1/chat/completions';
21
+ const prepared = coerceSnapshotPayloadForWrite(options.stage, options.data);
21
22
  void writeSnapshotViaHooks({
22
23
  endpoint,
23
24
  stage: options.stage,
24
25
  requestId: options.requestId,
25
26
  providerKey: options.providerKey,
26
27
  groupRequestId: options.groupRequestId,
27
- data: options.data,
28
+ data: prepared.data,
28
29
  verbosity: 'verbose'
29
30
  }).catch(() => {
30
31
  // ignore hook errors
31
32
  });
32
33
  }
34
+ const DEFAULT_SNAPSHOT_QUEUE_MAX_ITEMS = 10;
35
+ const SNAPSHOT_QUEUE_BATCH_SIZE = 64;
36
+ const DEFAULT_SNAPSHOT_PAYLOAD_MAX_BYTES = 256 * 1024;
37
+ const DEFAULT_SNAPSHOT_QUEUE_MEMORY_BUDGET_BYTES = 8 * 1024 * 1024;
38
+ const SNAPSHOT_QUEUE = [];
39
+ let snapshotQueueBytes = 0;
40
+ let snapshotQueueDrainScheduled = false;
41
+ function resolvePositiveIntegerFromEnv(names, fallback) {
42
+ for (const name of names) {
43
+ const raw = process.env[name];
44
+ const parsed = Number.parseInt(String(raw ?? '').trim(), 10);
45
+ if (Number.isFinite(parsed) && parsed > 0) {
46
+ return parsed;
47
+ }
48
+ }
49
+ return fallback;
50
+ }
51
+ function resolveSnapshotPayloadMaxBytes() {
52
+ return resolvePositiveIntegerFromEnv(['ROUTECODEX_SNAPSHOT_PAYLOAD_MAX_BYTES', 'RCC_SNAPSHOT_PAYLOAD_MAX_BYTES'], DEFAULT_SNAPSHOT_PAYLOAD_MAX_BYTES);
53
+ }
54
+ function resolveSnapshotQueueMemoryBudgetBytes() {
55
+ return resolvePositiveIntegerFromEnv(['ROUTECODEX_SNAPSHOT_QUEUE_MEMORY_BUDGET_BYTES', 'RCC_SNAPSHOT_QUEUE_MEMORY_BUDGET_BYTES'], DEFAULT_SNAPSHOT_QUEUE_MEMORY_BUDGET_BYTES);
56
+ }
57
+ function resolveSnapshotQueueMaxItems() {
58
+ return resolvePositiveIntegerFromEnv(['ROUTECODEX_SNAPSHOT_QUEUE_MAX_ITEMS', 'RCC_SNAPSHOT_QUEUE_MAX_ITEMS'], DEFAULT_SNAPSHOT_QUEUE_MAX_ITEMS);
59
+ }
60
+ function estimateSnapshotPayloadBytes(value, options) {
61
+ const maxBytes = options?.maxBytes ?? Number.POSITIVE_INFINITY;
62
+ const depth = options?.depth ?? 0;
63
+ const seen = options?.seen ?? new Set();
64
+ if (value === null || value === undefined) {
65
+ return 4;
66
+ }
67
+ const valueType = typeof value;
68
+ if (valueType === 'string') {
69
+ return Math.min(maxBytes + 1, value.length * 2 + 2);
70
+ }
71
+ if (valueType === 'number') {
72
+ return 8;
73
+ }
74
+ if (valueType === 'boolean') {
75
+ return 4;
76
+ }
77
+ if (valueType === 'bigint') {
78
+ return String(value).length + 8;
79
+ }
80
+ if (valueType === 'symbol' || valueType === 'function') {
81
+ return 16;
82
+ }
83
+ if (seen.has(value)) {
84
+ return 8;
85
+ }
86
+ seen.add(value);
87
+ if (depth >= 8) {
88
+ return 64;
89
+ }
90
+ let bytes = 0;
91
+ if (Array.isArray(value)) {
92
+ bytes += 2;
93
+ for (const item of value) {
94
+ bytes += estimateSnapshotPayloadBytes(item, {
95
+ maxBytes: Math.max(0, maxBytes - bytes),
96
+ depth: depth + 1,
97
+ seen
98
+ });
99
+ if (bytes > maxBytes) {
100
+ return maxBytes + 1;
101
+ }
102
+ }
103
+ return bytes;
104
+ }
105
+ if (value && typeof value === 'object') {
106
+ bytes += 2;
107
+ for (const [key, child] of Object.entries(value)) {
108
+ bytes += key.length * 2 + 4;
109
+ bytes += estimateSnapshotPayloadBytes(child, {
110
+ maxBytes: Math.max(0, maxBytes - bytes),
111
+ depth: depth + 1,
112
+ seen
113
+ });
114
+ if (bytes > maxBytes) {
115
+ return maxBytes + 1;
116
+ }
117
+ }
118
+ return bytes;
119
+ }
120
+ return 16;
121
+ }
122
+ function summarizeSnapshotPayload(value) {
123
+ if (Array.isArray(value)) {
124
+ return {
125
+ type: 'array',
126
+ length: value.length,
127
+ sampleTypes: value.slice(0, 8).map((item) => typeof item)
128
+ };
129
+ }
130
+ if (value && typeof value === 'object') {
131
+ const record = value;
132
+ const keys = Object.keys(record);
133
+ return {
134
+ type: 'object',
135
+ keyCount: keys.length,
136
+ keys: keys.slice(0, 24)
137
+ };
138
+ }
139
+ if (typeof value === 'string') {
140
+ return {
141
+ type: 'string',
142
+ length: value.length,
143
+ preview: value.length > 160 ? `${value.slice(0, 160)}…` : value
144
+ };
145
+ }
146
+ return {
147
+ type: typeof value,
148
+ value: value ?? null
149
+ };
150
+ }
151
+ function coerceSnapshotPayloadForWrite(stage, payload) {
152
+ const maxBytes = resolveSnapshotPayloadMaxBytes();
153
+ const estimatedBytes = estimateSnapshotPayloadBytes(payload, { maxBytes: maxBytes + 1 });
154
+ if (estimatedBytes <= maxBytes) {
155
+ return {
156
+ data: payload,
157
+ sizeBytes: Math.max(1, estimatedBytes)
158
+ };
159
+ }
160
+ return {
161
+ data: {
162
+ __snapshot_truncated: true,
163
+ stage,
164
+ maxBytes,
165
+ estimatedBytes,
166
+ summary: summarizeSnapshotPayload(payload)
167
+ },
168
+ sizeBytes: 512
169
+ };
170
+ }
171
+ function scheduleSnapshotQueueDrain() {
172
+ if (snapshotQueueDrainScheduled) {
173
+ return;
174
+ }
175
+ snapshotQueueDrainScheduled = true;
176
+ setImmediate(() => {
177
+ snapshotQueueDrainScheduled = false;
178
+ let processed = 0;
179
+ while (SNAPSHOT_QUEUE.length > 0 && processed < SNAPSHOT_QUEUE_BATCH_SIZE) {
180
+ const item = SNAPSHOT_QUEUE.shift();
181
+ if (!item) {
182
+ continue;
183
+ }
184
+ snapshotQueueBytes = Math.max(0, snapshotQueueBytes - Math.max(1, item.sizeBytes));
185
+ try {
186
+ item.task();
187
+ }
188
+ catch {
189
+ // snapshot write failures are non-blocking by design
190
+ }
191
+ processed += 1;
192
+ }
193
+ if (SNAPSHOT_QUEUE.length > 0) {
194
+ scheduleSnapshotQueueDrain();
195
+ }
196
+ });
197
+ }
198
+ function enqueueSnapshotTask(task, sizeBytes) {
199
+ const normalizedSize = Math.max(1, Math.floor(sizeBytes));
200
+ const queueBudgetBytes = resolveSnapshotQueueMemoryBudgetBytes();
201
+ const queueMaxItems = resolveSnapshotQueueMaxItems();
202
+ while (SNAPSHOT_QUEUE.length > 0
203
+ && (SNAPSHOT_QUEUE.length >= queueMaxItems || snapshotQueueBytes + normalizedSize > queueBudgetBytes)) {
204
+ const dropped = SNAPSHOT_QUEUE.shift();
205
+ if (!dropped) {
206
+ break;
207
+ }
208
+ snapshotQueueBytes = Math.max(0, snapshotQueueBytes - Math.max(1, dropped.sizeBytes));
209
+ }
210
+ SNAPSHOT_QUEUE.push({ task, sizeBytes: normalizedSize });
211
+ snapshotQueueBytes += normalizedSize;
212
+ scheduleSnapshotQueueDrain();
213
+ }
33
214
  export function createSnapshotWriter(opts) {
34
215
  if (!shouldRecordSnapshots()) {
35
216
  return undefined;
36
217
  }
37
218
  const endpoint = opts.endpoint || '/v1/chat/completions';
38
219
  return (stage, payload) => {
39
- void recordSnapshot({
40
- stage,
41
- requestId: opts.requestId,
42
- endpoint,
43
- folderHint: opts.folderHint,
44
- providerKey: opts.providerKey,
45
- groupRequestId: opts.groupRequestId,
46
- data: payload
47
- });
220
+ const prepared = coerceSnapshotPayloadForWrite(stage, payload);
221
+ enqueueSnapshotTask(() => {
222
+ void recordSnapshot({
223
+ stage,
224
+ requestId: opts.requestId,
225
+ endpoint,
226
+ folderHint: opts.folderHint,
227
+ providerKey: opts.providerKey,
228
+ groupRequestId: opts.groupRequestId,
229
+ data: prepared.data
230
+ });
231
+ }, prepared.sizeBytes);
48
232
  };
49
233
  }
@@ -153,10 +153,10 @@ export function applyErrorEvent(state, event, nowMs = event.timestampMs ?? Date.
153
153
  ? COOLDOWN_SCHEDULE_FATAL_MS
154
154
  : COOLDOWN_SCHEDULE_DEFAULT_MS;
155
155
  const rawNextCount = sameErrorKey ? state.consecutiveErrorCount + 1 : 1;
156
- const nextCount = rawNextCount > schedule.length ? 1 : rawNextCount;
156
+ const nextCount = Math.min(rawNextCount, Math.max(1, schedule.length));
157
157
  const cooldownMs = computeTransientKeepPoolCooldownMs(series, nextCount) ?? computeCooldownMsBySeries(series, nextCount);
158
158
  const nextUntil = cooldownMs ? nowMs + cooldownMs : null;
159
- const existingUntil = typeof state.cooldownUntil === 'number' ? state.cooldownUntil : null;
159
+ const existingUntil = sameErrorKey && typeof state.cooldownUntil === 'number' ? state.cooldownUntil : null;
160
160
  const cooldownUntil = typeof nextUntil === 'number' && Number.isFinite(nextUntil)
161
161
  ? typeof existingUntil === 'number' && existingUntil > nextUntil
162
162
  ? existingUntil
@@ -1,4 +1,36 @@
1
1
  import { mergeStopMessageFromPersisted } from '../../stop-message-state-sync.js';
2
+ import { providerErrorCenter } from '../../error-center.js';
3
+ function formatError(error) {
4
+ if (error instanceof Error) {
5
+ return error.message;
6
+ }
7
+ return String(error);
8
+ }
9
+ function emitRoutingStateRefreshError(key, error) {
10
+ const errorMessage = formatError(error);
11
+ providerErrorCenter.emit({
12
+ code: 'STICKY_STATE_REFRESH_FAILED',
13
+ message: 'failed to refresh in-memory routing state from persisted sticky store',
14
+ stage: 'sticky_session.refresh',
15
+ timestamp: Date.now(),
16
+ runtime: {
17
+ requestId: 'routing-state-store',
18
+ providerProtocol: 'sticky-session-store',
19
+ providerType: 'internal'
20
+ },
21
+ details: {
22
+ operation: 'refresh_existing_state',
23
+ key,
24
+ error: errorMessage
25
+ }
26
+ });
27
+ try {
28
+ console.warn(`[routing-state-store] STICKY_STATE_REFRESH_FAILED key=${key} error=${errorMessage}`);
29
+ }
30
+ catch {
31
+ // no-op
32
+ }
33
+ }
2
34
  function readToken(value) {
3
35
  if (typeof value !== 'string') {
4
36
  return '';
@@ -66,8 +98,9 @@ export function getRoutingInstructionState(stickyKey, routingInstructionState, r
66
98
  existing.preCommandUpdatedAt = persisted.preCommandUpdatedAt;
67
99
  }
68
100
  }
69
- catch {
70
- // 刷新失败不影响原有内存状态
101
+ catch (error) {
102
+ // 刷新失败不影响原有内存状态,但必须显式上报,禁止静默吞错
103
+ emitRoutingStateRefreshError(key, error);
71
104
  }
72
105
  return existing;
73
106
  }
@@ -64,20 +64,20 @@ export class VirtualRouterEngine {
64
64
  }
65
65
  const parsed = JSON.parse(raw);
66
66
  emitStopMessageMarkerParseLog(parseLog);
67
- // Keep legacy observable behavior for callers/tests that inspect the request object
68
- // after route(): instruction markers are stripped from forwarded payload structures.
69
67
  cleanStopMessageMarkersInPlace(request);
70
68
  const stopScope = parseLog?.stopScope || resolveStopMessageScope(metadata);
71
69
  const stopState = stopScope ? this.getStopMessageState(metadata) : null;
72
70
  const forceStopStatusLabel = Boolean(parseLog?.stopMessageTypes.length ||
73
71
  parseLog?.scopedTypes.some((type) => type === 'stopMessageSet' || type === 'stopMessageMode' || type === 'stopMessageClear'));
74
- emitVirtualRouterHitLog(parsed, {
75
- requestId: metadata.requestId,
76
- sessionId: resolveVirtualRouterLogSessionId(metadata),
77
- stopScope,
78
- stopState,
79
- forceStopStatusLabel
80
- });
72
+ if (metadata.__rt?.disableVirtualRouterHitLog !== true) {
73
+ emitVirtualRouterHitLog(parsed, {
74
+ requestId: metadata.requestId,
75
+ sessionId: resolveVirtualRouterLogSessionId(metadata),
76
+ stopScope,
77
+ stopState,
78
+ forceStopStatusLabel
79
+ });
80
+ }
81
81
  return parsed;
82
82
  }
83
83
  getStopMessageState(metadata) {