@jsonstudio/rcc 0.90.814 → 0.90.872

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 (217) 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 +16 -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 +121 -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/docs/INSTALLATION_AND_QUICKSTART.md +14 -1
  156. package/docs/PORTS.md +12 -0
  157. package/docs/lmstudio-tool-calling.md +25 -0
  158. package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/qwenchat-web-request.d.ts +3 -0
  159. package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/qwenchat-web-request.js +62 -0
  160. package/node_modules/@jsonstudio/llms/dist/conversion/compat/profiles/chat-qwenchat-web.json +47 -0
  161. package/node_modules/@jsonstudio/llms/dist/conversion/hub/operation-table/operation-table-runner.js +68 -7
  162. package/node_modules/@jsonstudio/llms/dist/conversion/hub/operation-table/semantic-mappers/anthropic-mapper-from-chat.js +138 -3
  163. package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/hub-pipeline-chat-process-request-utils.js +24 -0
  164. package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/hub-pipeline-execute-chat-process-entry.js +7 -1
  165. package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/hub-pipeline-execute-request-stage.js +7 -0
  166. package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/hub-pipeline-heavy-input-fastpath.d.ts +24 -0
  167. package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/hub-pipeline-heavy-input-fastpath.js +203 -0
  168. package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/hub-pipeline-route-and-outbound.js +17 -12
  169. package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/hub-stage-timing.d.ts +11 -0
  170. package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/hub-stage-timing.js +82 -1
  171. package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage2_semantic_map/index.js +47 -14
  172. package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage1_semantic_map/index.js +43 -0
  173. package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/stages/resp_outbound/resp_outbound_stage1_client_remap/client-remap-protocol-switch.js +222 -19
  174. package/node_modules/@jsonstudio/llms/dist/conversion/hub/policy/policy-engine.js +2 -2
  175. package/node_modules/@jsonstudio/llms/dist/conversion/hub/process/chat-process-pending-tool-sync.js +24 -7
  176. package/node_modules/@jsonstudio/llms/dist/conversion/hub/response/provider-response.js +90 -1
  177. package/node_modules/@jsonstudio/llms/dist/conversion/hub/snapshot-recorder.d.ts +1 -0
  178. package/node_modules/@jsonstudio/llms/dist/conversion/hub/snapshot-recorder.js +252 -1
  179. package/node_modules/@jsonstudio/llms/dist/conversion/responses/responses-openai-bridge/utils.js +5 -3
  180. package/node_modules/@jsonstudio/llms/dist/conversion/responses/responses-openai-bridge.js +44 -4
  181. package/node_modules/@jsonstudio/llms/dist/conversion/shared/anthropic-message-utils-openai-request.d.ts +3 -1
  182. package/node_modules/@jsonstudio/llms/dist/conversion/shared/anthropic-message-utils-openai-request.js +20 -23
  183. package/node_modules/@jsonstudio/llms/dist/conversion/shared/tool-governor.js +68 -30
  184. package/node_modules/@jsonstudio/llms/dist/conversion/snapshot-utils.js +48 -8
  185. package/node_modules/@jsonstudio/llms/dist/native/router_hotpath_napi.node +0 -0
  186. package/node_modules/@jsonstudio/llms/dist/quota/quota-state.js +2 -2
  187. package/node_modules/@jsonstudio/llms/dist/router/virtual-router/engine/routing-state/store.js +35 -2
  188. package/node_modules/@jsonstudio/llms/dist/router/virtual-router/engine.js +9 -9
  189. package/node_modules/@jsonstudio/llms/dist/router/virtual-router/sticky-session-store.js +104 -18
  190. package/node_modules/@jsonstudio/llms/dist/servertool/engine.js +79 -32
  191. package/node_modules/@jsonstudio/llms/dist/servertool/handlers/vision.js +49 -0
  192. package/node_modules/@jsonstudio/llms/dist/servertool/pending-session.js +48 -2
  193. package/node_modules/@jsonstudio/llms/dist/servertool/server-side-tools.js +14 -1
  194. package/node_modules/@jsonstudio/llms/dist/servertool/types.d.ts +1 -0
  195. package/node_modules/@jsonstudio/llms/package.json +1 -1
  196. package/node_modules/ajv/dist/compile/jtd/serialize.js +9 -2
  197. package/node_modules/ajv/dist/compile/jtd/serialize.js.map +1 -1
  198. package/node_modules/ajv/dist/core.d.ts +1 -0
  199. package/node_modules/ajv/dist/core.js.map +1 -1
  200. package/node_modules/ajv/dist/vocabularies/validation/pattern.js +13 -4
  201. package/node_modules/ajv/dist/vocabularies/validation/pattern.js.map +1 -1
  202. package/node_modules/ajv/lib/compile/jtd/serialize.ts +13 -2
  203. package/node_modules/ajv/lib/core.ts +1 -0
  204. package/node_modules/ajv/lib/vocabularies/validation/pattern.ts +15 -4
  205. package/node_modules/ajv/package.json +2 -1
  206. package/package.json +15 -10
  207. package/scripts/ci/repo-sanity.mjs +23 -2
  208. package/scripts/ci/secrets-check.mjs +48 -0
  209. package/scripts/ci/silent-failure-audit.mjs +192 -0
  210. package/scripts/mock-provider/run-regressions.mjs +1 -0
  211. package/scripts/pack-mode.mjs +32 -36
  212. package/scripts/publish-rcc.mjs +38 -60
  213. package/scripts/tests/apply-patch-loop.mjs +1 -0
  214. package/scripts/tests/blackbox-rcc-vs-routecodex-antigravity.mjs +2 -0
  215. package/scripts/tools-dev/responses-debug-client/src/index.ts +8 -3
  216. package/scripts/verify-e2e-toolcall.mjs +1 -0
  217. 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
  }
@@ -30,20 +30,60 @@ export async function recordSnapshot(options) {
30
30
  // ignore hook errors
31
31
  });
32
32
  }
33
+ const MAX_SNAPSHOT_QUEUE_SIZE = 2048;
34
+ const SNAPSHOT_QUEUE_BATCH_SIZE = 64;
35
+ const SNAPSHOT_QUEUE = [];
36
+ let snapshotQueueDrainScheduled = false;
37
+ function scheduleSnapshotQueueDrain() {
38
+ if (snapshotQueueDrainScheduled) {
39
+ return;
40
+ }
41
+ snapshotQueueDrainScheduled = true;
42
+ setImmediate(() => {
43
+ snapshotQueueDrainScheduled = false;
44
+ let processed = 0;
45
+ while (SNAPSHOT_QUEUE.length > 0 && processed < SNAPSHOT_QUEUE_BATCH_SIZE) {
46
+ const task = SNAPSHOT_QUEUE.shift();
47
+ if (!task) {
48
+ continue;
49
+ }
50
+ try {
51
+ task();
52
+ }
53
+ catch {
54
+ // snapshot write failures are non-blocking by design
55
+ }
56
+ processed += 1;
57
+ }
58
+ if (SNAPSHOT_QUEUE.length > 0) {
59
+ scheduleSnapshotQueueDrain();
60
+ }
61
+ });
62
+ }
63
+ function enqueueSnapshotTask(task) {
64
+ if (SNAPSHOT_QUEUE.length >= MAX_SNAPSHOT_QUEUE_SIZE) {
65
+ // keep newest writes; snapshot stream is best-effort and must not block request hot path
66
+ SNAPSHOT_QUEUE.shift();
67
+ }
68
+ SNAPSHOT_QUEUE.push(task);
69
+ scheduleSnapshotQueueDrain();
70
+ }
33
71
  export function createSnapshotWriter(opts) {
34
72
  if (!shouldRecordSnapshots()) {
35
73
  return undefined;
36
74
  }
37
75
  const endpoint = opts.endpoint || '/v1/chat/completions';
38
76
  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
77
+ enqueueSnapshotTask(() => {
78
+ void recordSnapshot({
79
+ stage,
80
+ requestId: opts.requestId,
81
+ endpoint,
82
+ folderHint: opts.folderHint,
83
+ providerKey: opts.providerKey,
84
+ groupRequestId: opts.groupRequestId,
85
+ data: payload
86
+ });
47
87
  });
48
88
  };
49
89
  }
@@ -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) {
@@ -1,8 +1,44 @@
1
1
  import * as fs from 'node:fs';
2
2
  import * as path from 'node:path';
3
3
  import { serializeRoutingInstructionState, deserializeRoutingInstructionState } from './routing-instructions.js';
4
+ import { providerErrorCenter } from './error-center.js';
4
5
  import { resolveRccPath } from '../../runtime/user-data-paths.js';
5
6
  const pendingWrites = new Map();
7
+ const STICKY_RUNTIME_REQUEST_ID = 'sticky-session-store';
8
+ function isNodeErrorWithCode(error) {
9
+ return !!error && typeof error === 'object' && 'code' in error;
10
+ }
11
+ function shouldIgnoreUnlinkError(error) {
12
+ return isNodeErrorWithCode(error) && error.code === 'ENOENT';
13
+ }
14
+ function formatError(error) {
15
+ if (error instanceof Error) {
16
+ return error.message;
17
+ }
18
+ return String(error);
19
+ }
20
+ function emitStickyStoreError(code, stage, message, details) {
21
+ providerErrorCenter.emit({
22
+ code,
23
+ message,
24
+ stage,
25
+ timestamp: Date.now(),
26
+ runtime: {
27
+ requestId: STICKY_RUNTIME_REQUEST_ID,
28
+ providerProtocol: 'sticky-session-store',
29
+ providerType: 'internal'
30
+ },
31
+ details
32
+ });
33
+ try {
34
+ const op = typeof details.operation === 'string' ? details.operation : 'unknown';
35
+ const errMsg = typeof details.error === 'string' ? details.error : '';
36
+ console.warn(`[sticky-session-store] ${code} stage=${stage} op=${op}${errMsg ? ` error=${errMsg}` : ''}`);
37
+ }
38
+ catch {
39
+ // no-op
40
+ }
41
+ }
6
42
  export class StickySessionKeyMissingError extends Error {
7
43
  key;
8
44
  constructor(key, message) {
@@ -78,9 +114,14 @@ function readPersistedStateFromFile(filepath) {
78
114
  try {
79
115
  parsed = JSON.parse(raw);
80
116
  }
81
- catch {
117
+ catch (parseError) {
82
118
  const recovered = recoverPersistedJson(raw);
83
119
  if (!recovered) {
120
+ emitStickyStoreError('STICKY_STATE_READ_FAILED', 'sticky_session.read', 'failed to parse persisted sticky state JSON', {
121
+ operation: 'read_parse_json',
122
+ filepath,
123
+ error: formatError(parseError)
124
+ });
84
125
  return null;
85
126
  }
86
127
  parsed = recovered;
@@ -90,8 +131,12 @@ function readPersistedStateFromFile(filepath) {
90
131
  : { version: 1, state: parsed };
91
132
  atomicWriteFileSync(filepath, JSON.stringify(payload));
92
133
  }
93
- catch {
94
- // ignore rewrite failures
134
+ catch (rewriteError) {
135
+ emitStickyStoreError('STICKY_STATE_RECOVER_FAILED', 'sticky_session.recover', 'failed to rewrite recovered sticky state payload', {
136
+ operation: 'recover_rewrite',
137
+ filepath,
138
+ error: formatError(rewriteError)
139
+ });
95
140
  }
96
141
  }
97
142
  const payload = parsed && typeof parsed.version === 'number'
@@ -102,7 +147,12 @@ function readPersistedStateFromFile(filepath) {
102
147
  }
103
148
  return deserializeRoutingInstructionState(payload);
104
149
  }
105
- catch {
150
+ catch (error) {
151
+ emitStickyStoreError('STICKY_STATE_READ_FAILED', 'sticky_session.read', 'failed to read sticky session state from disk', {
152
+ operation: 'read_file',
153
+ filepath,
154
+ error: formatError(error)
155
+ });
106
156
  return null;
107
157
  }
108
158
  }
@@ -137,8 +187,14 @@ export function saveRoutingInstructionStateAsync(key, state) {
137
187
  try {
138
188
  await fs.promises.unlink(filepath);
139
189
  }
140
- catch {
141
- // ignore unlink errors (e.g. ENOENT)
190
+ catch (error) {
191
+ if (!shouldIgnoreUnlinkError(error)) {
192
+ emitStickyStoreError('STICKY_STATE_PERSIST_FAILED', 'sticky_session.persist', 'failed to unlink sticky session state file', {
193
+ operation: 'unlink',
194
+ filepath,
195
+ error: formatError(error)
196
+ });
197
+ }
142
198
  }
143
199
  });
144
200
  }
@@ -154,14 +210,24 @@ export function saveRoutingInstructionStateAsync(key, state) {
154
210
  try {
155
211
  await fs.promises.mkdir(dir, { recursive: true });
156
212
  }
157
- catch {
158
- // ignore mkdir errors; write below will fail silently
213
+ catch (error) {
214
+ emitStickyStoreError('STICKY_STATE_PERSIST_FAILED', 'sticky_session.persist', 'failed to create sticky session state directory', {
215
+ operation: 'mkdir',
216
+ filepath,
217
+ dir,
218
+ error: formatError(error)
219
+ });
159
220
  }
160
221
  try {
161
222
  await atomicWriteFile(filepath, JSON.stringify(payload));
162
223
  }
163
- catch {
164
- // ignore async write failures
224
+ catch (error) {
225
+ emitStickyStoreError('STICKY_STATE_PERSIST_FAILED', 'sticky_session.persist', 'failed to persist sticky session state file', {
226
+ operation: 'write',
227
+ filepath,
228
+ dir,
229
+ error: formatError(error)
230
+ });
165
231
  }
166
232
  });
167
233
  }
@@ -179,8 +245,14 @@ export function saveRoutingInstructionStateSync(key, state) {
179
245
  try {
180
246
  fs.unlinkSync(filepath);
181
247
  }
182
- catch {
183
- // ignore unlink failures
248
+ catch (error) {
249
+ if (!shouldIgnoreUnlinkError(error)) {
250
+ emitStickyStoreError('STICKY_STATE_PERSIST_FAILED', 'sticky_session.persist', 'failed to unlink sticky session state file', {
251
+ operation: 'unlinkSync',
252
+ filepath,
253
+ error: formatError(error)
254
+ });
255
+ }
184
256
  }
185
257
  }
186
258
  return;
@@ -194,14 +266,24 @@ export function saveRoutingInstructionStateSync(key, state) {
194
266
  try {
195
267
  fs.mkdirSync(dir, { recursive: true });
196
268
  }
197
- catch {
198
- // ignore mkdir errors
269
+ catch (error) {
270
+ emitStickyStoreError('STICKY_STATE_PERSIST_FAILED', 'sticky_session.persist', 'failed to create sticky session state directory', {
271
+ operation: 'mkdirSync',
272
+ filepath,
273
+ dir,
274
+ error: formatError(error)
275
+ });
199
276
  }
200
277
  try {
201
278
  atomicWriteFileSync(filepath, JSON.stringify(payload));
202
279
  }
203
- catch {
204
- // ignore sync write failures
280
+ catch (error) {
281
+ emitStickyStoreError('STICKY_STATE_PERSIST_FAILED', 'sticky_session.persist', 'failed to persist sticky session state file', {
282
+ operation: 'writeSync',
283
+ filepath,
284
+ dir,
285
+ error: formatError(error)
286
+ });
205
287
  }
206
288
  }
207
289
  }
@@ -209,8 +291,12 @@ function scheduleWrite(filepath, task) {
209
291
  const previous = pendingWrites.get(filepath) ?? Promise.resolve();
210
292
  const next = previous
211
293
  .then(task)
212
- .catch(() => {
213
- // swallow errors
294
+ .catch((error) => {
295
+ emitStickyStoreError('STICKY_STATE_PERSIST_FAILED', 'sticky_session.persist', 'unexpected sticky session async write task failure', {
296
+ operation: 'scheduleWrite',
297
+ filepath,
298
+ error: formatError(error)
299
+ });
214
300
  })
215
301
  .finally(() => {
216
302
  if (pendingWrites.get(filepath) === next) {