@jsonstudio/rcc 0.89.1189 → 0.89.1348

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 (337) hide show
  1. package/README.md +17 -0
  2. package/configsamples/config.json +426 -0
  3. package/configsamples/config.reference.json +58 -0
  4. package/configsamples/provider/crs/config.v1.json +46 -0
  5. package/configsamples/provider/glm/config.v1.json +81 -0
  6. package/configsamples/provider/glm-anthropic/config.v1.json +45 -0
  7. package/configsamples/provider/iflow/config.v1.json +74 -0
  8. package/configsamples/provider/kimi/config.v1.json +41 -0
  9. package/configsamples/provider/lmstudio/config.v1.json +101 -0
  10. package/configsamples/provider/mimo/config.v1.json +35 -0
  11. package/configsamples/provider/modelscope/config.v1.json +96 -0
  12. package/configsamples/provider/qwen/config.v1.json +38 -0
  13. package/configsamples/provider/tab/config.v1.json +50 -0
  14. package/configsamples/provider/tabglm/config.v1.json +49 -0
  15. package/dist/build-info.js +2 -2
  16. package/dist/cli/commands/code.js +12 -6
  17. package/dist/cli/commands/code.js.map +1 -1
  18. package/dist/cli/commands/config.d.ts +2 -1
  19. package/dist/cli/commands/config.js +74 -103
  20. package/dist/cli/commands/config.js.map +1 -1
  21. package/dist/cli/commands/examples.js +6 -6
  22. package/dist/cli/commands/examples.js.map +1 -1
  23. package/dist/cli/commands/init.d.ts +28 -0
  24. package/dist/cli/commands/init.js +91 -0
  25. package/dist/cli/commands/init.js.map +1 -0
  26. package/dist/cli/commands/port.js +10 -2
  27. package/dist/cli/commands/port.js.map +1 -1
  28. package/dist/cli/commands/restart.js +5 -2
  29. package/dist/cli/commands/restart.js.map +1 -1
  30. package/dist/cli/commands/start.js +25 -22
  31. package/dist/cli/commands/start.js.map +1 -1
  32. package/dist/cli/commands/status.js +1 -0
  33. package/dist/cli/commands/status.js.map +1 -1
  34. package/dist/cli/commands/stop.js +1 -0
  35. package/dist/cli/commands/stop.js.map +1 -1
  36. package/dist/cli/config/bundled-docs.d.ts +20 -0
  37. package/dist/cli/config/bundled-docs.js +91 -0
  38. package/dist/cli/config/bundled-docs.js.map +1 -0
  39. package/dist/cli/config/init-config.d.ts +36 -0
  40. package/dist/cli/config/init-config.js +180 -0
  41. package/dist/cli/config/init-config.js.map +1 -0
  42. package/dist/cli/config/init-provider-catalog.d.ts +8 -0
  43. package/dist/cli/config/init-provider-catalog.js +187 -0
  44. package/dist/cli/config/init-provider-catalog.js.map +1 -0
  45. package/dist/cli/register/init-command.d.ts +3 -0
  46. package/dist/cli/register/init-command.js +5 -0
  47. package/dist/cli/register/init-command.js.map +1 -0
  48. package/dist/cli.js +28 -3
  49. package/dist/cli.js.map +1 -1
  50. package/dist/client/gemini-cli/gemini-cli-protocol-client.js +1 -1
  51. package/dist/client/gemini-cli/gemini-cli-protocol-client.js.map +1 -1
  52. package/dist/config/risk-control-config.d.ts +94 -0
  53. package/dist/config/risk-control-config.js +196 -0
  54. package/dist/config/risk-control-config.js.map +1 -0
  55. package/dist/constants/index.d.ts +6 -0
  56. package/dist/constants/index.js +13 -0
  57. package/dist/constants/index.js.map +1 -1
  58. package/dist/docs/daemon-admin-ui.html +2113 -190
  59. package/dist/index.js +0 -1
  60. package/dist/index.js.map +1 -1
  61. package/dist/manager/modules/health/index.d.ts +1 -1
  62. package/dist/manager/modules/quota/antigravity-quota-manager.d.ts +70 -0
  63. package/dist/manager/modules/quota/antigravity-quota-manager.js +442 -0
  64. package/dist/manager/modules/quota/antigravity-quota-manager.js.map +1 -0
  65. package/dist/manager/modules/quota/index.d.ts +3 -127
  66. package/dist/manager/modules/quota/index.js +2 -1093
  67. package/dist/manager/modules/quota/index.js.map +1 -1
  68. package/dist/manager/modules/quota/provider-key-normalization.d.ts +3 -0
  69. package/dist/manager/modules/quota/provider-key-normalization.js +155 -0
  70. package/dist/manager/modules/quota/provider-key-normalization.js.map +1 -0
  71. package/dist/manager/modules/quota/provider-quota-daemon.cooldown.d.ts +9 -0
  72. package/dist/manager/modules/quota/provider-quota-daemon.cooldown.js +115 -0
  73. package/dist/manager/modules/quota/provider-quota-daemon.cooldown.js.map +1 -0
  74. package/dist/manager/modules/quota/provider-quota-daemon.d.ts +77 -0
  75. package/dist/manager/modules/quota/provider-quota-daemon.events.d.ts +12 -0
  76. package/dist/manager/modules/quota/provider-quota-daemon.events.js +237 -0
  77. package/dist/manager/modules/quota/provider-quota-daemon.events.js.map +1 -0
  78. package/dist/manager/modules/quota/provider-quota-daemon.js +404 -0
  79. package/dist/manager/modules/quota/provider-quota-daemon.js.map +1 -0
  80. package/dist/manager/modules/quota/provider-quota-daemon.model-backoff.d.ts +11 -0
  81. package/dist/manager/modules/quota/provider-quota-daemon.model-backoff.js +189 -0
  82. package/dist/manager/modules/quota/provider-quota-daemon.model-backoff.js.map +1 -0
  83. package/dist/manager/modules/quota/provider-quota-daemon.snapshot.d.ts +8 -0
  84. package/dist/manager/modules/quota/provider-quota-daemon.snapshot.js +96 -0
  85. package/dist/manager/modules/quota/provider-quota-daemon.snapshot.js.map +1 -0
  86. package/dist/manager/modules/quota/provider-quota-daemon.view.d.ts +19 -0
  87. package/dist/manager/modules/quota/provider-quota-daemon.view.js +37 -0
  88. package/dist/manager/modules/quota/provider-quota-daemon.view.js.map +1 -0
  89. package/dist/manager/modules/routing/index.d.ts +1 -0
  90. package/dist/manager/modules/routing/index.js +11 -25
  91. package/dist/manager/modules/routing/index.js.map +1 -1
  92. package/dist/manager/quota/provider-quota-center.d.ts +2 -0
  93. package/dist/manager/quota/provider-quota-center.js +80 -82
  94. package/dist/manager/quota/provider-quota-center.js.map +1 -1
  95. package/dist/modules/llmswitch/bridge.d.ts +16 -18
  96. package/dist/modules/llmswitch/bridge.js +314 -71
  97. package/dist/modules/llmswitch/bridge.js.map +1 -1
  98. package/dist/modules/llmswitch/core-loader.d.ts +4 -2
  99. package/dist/modules/llmswitch/core-loader.js +32 -20
  100. package/dist/modules/llmswitch/core-loader.js.map +1 -1
  101. package/dist/modules/pipeline/utils/colored-logger.js +3 -2
  102. package/dist/modules/pipeline/utils/colored-logger.js.map +1 -1
  103. package/dist/modules/pipeline/utils/debug-logger.js +1 -1
  104. package/dist/modules/pipeline/utils/debug-logger.js.map +1 -1
  105. package/dist/providers/auth/iflow-cookie-auth.js +0 -2
  106. package/dist/providers/auth/iflow-cookie-auth.js.map +1 -1
  107. package/dist/providers/auth/oauth-lifecycle.js +2 -23
  108. package/dist/providers/auth/oauth-lifecycle.js.map +1 -1
  109. package/dist/providers/core/config/camoufox-launcher.js +35 -4
  110. package/dist/providers/core/config/camoufox-launcher.js.map +1 -1
  111. package/dist/providers/core/runtime/antigravity-quota-client.js +6 -3
  112. package/dist/providers/core/runtime/antigravity-quota-client.js.map +1 -1
  113. package/dist/providers/core/runtime/base-provider.d.ts +2 -2
  114. package/dist/providers/core/runtime/base-provider.js +74 -69
  115. package/dist/providers/core/runtime/base-provider.js.map +1 -1
  116. package/dist/providers/core/runtime/gemini-cli-http-provider.js +6 -4
  117. package/dist/providers/core/runtime/gemini-cli-http-provider.js.map +1 -1
  118. package/dist/providers/core/runtime/http-request-executor.js +2 -2
  119. package/dist/providers/core/runtime/http-request-executor.js.map +1 -1
  120. package/dist/providers/core/runtime/http-transport-provider.d.ts +14 -0
  121. package/dist/providers/core/runtime/http-transport-provider.js +111 -5
  122. package/dist/providers/core/runtime/http-transport-provider.js.map +1 -1
  123. package/dist/providers/core/runtime/provider-error-classifier.js +10 -0
  124. package/dist/providers/core/runtime/provider-error-classifier.js.map +1 -1
  125. package/dist/providers/core/runtime/provider-factory.js +7 -5
  126. package/dist/providers/core/runtime/provider-factory.js.map +1 -1
  127. package/dist/providers/core/runtime/provider-runtime-metadata.d.ts +6 -0
  128. package/dist/providers/core/runtime/provider-runtime-metadata.js.map +1 -1
  129. package/dist/providers/core/runtime/responses-provider.d.ts +1 -7
  130. package/dist/providers/core/runtime/responses-provider.js +12 -93
  131. package/dist/providers/core/runtime/responses-provider.js.map +1 -1
  132. package/dist/providers/core/strategies/oauth-auth-code-flow.js +12 -8
  133. package/dist/providers/core/strategies/oauth-auth-code-flow.js.map +1 -1
  134. package/dist/providers/core/utils/http-client.js +16 -3
  135. package/dist/providers/core/utils/http-client.js.map +1 -1
  136. package/dist/providers/core/utils/provider-error-logger.d.ts +1 -1
  137. package/dist/providers/core/utils/provider-error-reporter.d.ts +3 -1
  138. package/dist/providers/core/utils/provider-error-reporter.js +3 -0
  139. package/dist/providers/core/utils/provider-error-reporter.js.map +1 -1
  140. package/dist/providers/core/utils/snapshot-writer.js +1 -4
  141. package/dist/providers/core/utils/snapshot-writer.js.map +1 -1
  142. package/dist/providers/mock/mock-provider-runtime.js +57 -27
  143. package/dist/providers/mock/mock-provider-runtime.js.map +1 -1
  144. package/dist/scripts/camoufox/launch-auth.mjs +193 -58
  145. package/dist/server/handlers/handler-utils.js +3 -2
  146. package/dist/server/handlers/handler-utils.js.map +1 -1
  147. package/dist/server/runtime/http-server/daemon-admin/auth-handler.d.ts +2 -0
  148. package/dist/server/runtime/http-server/daemon-admin/auth-handler.js +103 -0
  149. package/dist/server/runtime/http-server/daemon-admin/auth-handler.js.map +1 -0
  150. package/dist/server/runtime/http-server/daemon-admin/auth-session.d.ts +5 -0
  151. package/dist/server/runtime/http-server/daemon-admin/auth-session.js +77 -0
  152. package/dist/server/runtime/http-server/daemon-admin/auth-session.js.map +1 -0
  153. package/dist/server/runtime/http-server/daemon-admin/auth-store.d.ts +18 -0
  154. package/dist/server/runtime/http-server/daemon-admin/auth-store.js +89 -0
  155. package/dist/server/runtime/http-server/daemon-admin/auth-store.js.map +1 -0
  156. package/dist/server/runtime/http-server/daemon-admin/credentials-handler.js +1 -2
  157. package/dist/server/runtime/http-server/daemon-admin/credentials-handler.js.map +1 -1
  158. package/dist/server/runtime/http-server/daemon-admin/providers-handler.js +226 -24
  159. package/dist/server/runtime/http-server/daemon-admin/providers-handler.js.map +1 -1
  160. package/dist/server/runtime/http-server/daemon-admin/quota-handler.js +47 -8
  161. package/dist/server/runtime/http-server/daemon-admin/quota-handler.js.map +1 -1
  162. package/dist/server/runtime/http-server/daemon-admin/restart-handler.js +1 -1
  163. package/dist/server/runtime/http-server/daemon-admin/restart-handler.js.map +1 -1
  164. package/dist/server/runtime/http-server/daemon-admin/stats-handler.js +1 -1
  165. package/dist/server/runtime/http-server/daemon-admin/stats-handler.js.map +1 -1
  166. package/dist/server/runtime/http-server/daemon-admin/status-handler.js +68 -4
  167. package/dist/server/runtime/http-server/daemon-admin/status-handler.js.map +1 -1
  168. package/dist/server/runtime/http-server/daemon-admin-routes.d.ts +3 -4
  169. package/dist/server/runtime/http-server/daemon-admin-routes.js +9 -14
  170. package/dist/server/runtime/http-server/daemon-admin-routes.js.map +1 -1
  171. package/dist/server/runtime/http-server/executor-metadata.js +1 -1
  172. package/dist/server/runtime/http-server/executor-metadata.js.map +1 -1
  173. package/dist/server/runtime/http-server/executor-response.js +0 -16
  174. package/dist/server/runtime/http-server/executor-response.js.map +1 -1
  175. package/dist/server/runtime/http-server/hub-shadow-compare.d.ts +18 -0
  176. package/dist/server/runtime/http-server/hub-shadow-compare.js +256 -0
  177. package/dist/server/runtime/http-server/hub-shadow-compare.js.map +1 -0
  178. package/dist/server/runtime/http-server/index.d.ts +7 -2
  179. package/dist/server/runtime/http-server/index.js +287 -49
  180. package/dist/server/runtime/http-server/index.js.map +1 -1
  181. package/dist/server/runtime/http-server/middleware.js +19 -1
  182. package/dist/server/runtime/http-server/middleware.js.map +1 -1
  183. package/dist/server/runtime/http-server/request-executor.js +10 -19
  184. package/dist/server/runtime/http-server/request-executor.js.map +1 -1
  185. package/dist/server/runtime/http-server/routes.js +8 -2
  186. package/dist/server/runtime/http-server/routes.js.map +1 -1
  187. package/dist/server/runtime/http-server/session-dir.d.ts +2 -0
  188. package/dist/server/runtime/http-server/session-dir.js +59 -0
  189. package/dist/server/runtime/http-server/session-dir.js.map +1 -0
  190. package/dist/server/runtime/http-server/types.d.ts +0 -4
  191. package/dist/server/utils/utf8-chunk-buffer.js +6 -3
  192. package/dist/server/utils/utf8-chunk-buffer.js.map +1 -1
  193. package/dist/server/utils/warmup-storm-tracker.js +1 -1
  194. package/dist/server/utils/warmup-storm-tracker.js.map +1 -1
  195. package/dist/server-factory.d.ts +6 -28
  196. package/dist/server-factory.js +8 -93
  197. package/dist/server-factory.js.map +1 -1
  198. package/dist/token-daemon/index.js +2 -2
  199. package/dist/token-daemon/index.js.map +1 -1
  200. package/dist/token-daemon/provider-registry.js +0 -1
  201. package/dist/token-daemon/provider-registry.js.map +1 -1
  202. package/dist/token-daemon/server-utils.js +8 -9
  203. package/dist/token-daemon/server-utils.js.map +1 -1
  204. package/dist/token-daemon/token-utils.js +1 -1
  205. package/dist/token-daemon/token-utils.js.map +1 -1
  206. package/dist/tools/semantic-replay.js +2 -2
  207. package/dist/tools/semantic-replay.js.map +1 -1
  208. package/dist/tools/stats-request-events.d.ts +1 -1
  209. package/dist/tools/stats-usage.js +6 -3
  210. package/dist/tools/stats-usage.js.map +1 -1
  211. package/dist/utils/errorsamples.d.ts +5 -0
  212. package/dist/utils/errorsamples.js +27 -0
  213. package/dist/utils/errorsamples.js.map +1 -0
  214. package/dist/utils/llms-engine-shadow.d.ts +19 -0
  215. package/dist/utils/llms-engine-shadow.js +209 -0
  216. package/dist/utils/llms-engine-shadow.js.map +1 -0
  217. package/dist/utils/runtime-versions.d.ts +1 -0
  218. package/dist/utils/runtime-versions.js +39 -0
  219. package/dist/utils/runtime-versions.js.map +1 -0
  220. package/docs/ARCHITECTURE.md +402 -0
  221. package/docs/CODEX_AND_CLAUDE_CODE.md +69 -0
  222. package/docs/CONFIG_ARCHITECTURE.md +517 -0
  223. package/docs/ERROR_HANDLING_AUDIT.md +0 -0
  224. package/docs/GCLI2API_PARITY_GAPS.md +98 -0
  225. package/docs/INSTALLATION_AND_QUICKSTART.md +74 -0
  226. package/docs/INSTRUCTION_MARKUP.md +89 -0
  227. package/docs/MODULE_ENHANCEMENT_SYSTEM.md +666 -0
  228. package/docs/PORTS.md +36 -0
  229. package/docs/PROVIDERS_BUILTIN.md +111 -0
  230. package/docs/PROVIDER_TYPES.md +55 -0
  231. package/docs/SERVERTOOL_CLOCK_DESIGN.md +233 -0
  232. package/docs/USAGE_HANDLING_ANALYSIS.md +335 -0
  233. package/docs/USER_CONFIG_PARSER_CHANGES.md +175 -0
  234. package/docs/V3_INBOUND_OUTBOUND_DESIGN.md +86 -0
  235. package/docs/VIRTUAL_ROUTER_PRIORITY_AND_HEALTH.md +125 -0
  236. package/docs/anthropic-request-golden-samples.md +50 -0
  237. package/docs/ccr-alignment-enhancetool.md +105 -0
  238. package/docs/chat-glm-500-analysis.md +79 -0
  239. package/docs/chat-request-golden-samples.md +42 -0
  240. package/docs/chat-semantic-expansion-plan.md +82 -0
  241. package/docs/cli-command-inventory.md +76 -0
  242. package/docs/codex-samples-replay.md +50 -0
  243. package/docs/daemon-admin-api-design.md +350 -0
  244. package/docs/daemon-admin-module-structure.md +169 -0
  245. package/docs/daemon-admin-ui.html +3394 -0
  246. package/docs/debug-system-design.md +734 -0
  247. package/docs/debugging/gemini-sse-root-cause.md +52 -0
  248. package/docs/debugging/sse_encoding_failure_analysis.md +53 -0
  249. package/docs/dry-run/README.md +721 -0
  250. package/docs/error-handling-v2.md +92 -0
  251. package/docs/exec-command-guard-policy.example.v1.json +42 -0
  252. package/docs/fixes/gemini-protocol-mapping.md +57 -0
  253. package/docs/fixes/oauth-portal-timing-fix.md +202 -0
  254. package/docs/fixes/web-search-hop3-fix.md +265 -0
  255. package/docs/glm-api-reference.md +390 -0
  256. package/docs/glm-chat-completions.md +1779 -0
  257. package/docs/glm-history-inline-images.md +44 -0
  258. package/docs/golden-ci-library.md +66 -0
  259. package/docs/lmstudio-dry-run-summary.md +203 -0
  260. package/docs/lmstudio-tool-calling.md +214 -0
  261. package/docs/mapping-tables/anthropic-to-openai.json +290 -0
  262. package/docs/mapping-tables/iflow-to-openai.json +215 -0
  263. package/docs/mapping-tables/openai-passthrough.json +190 -0
  264. package/docs/mapping-tables/openai-to-iflow.json +227 -0
  265. package/docs/monitoring/Design.md +61 -0
  266. package/docs/multi-token-auth-guide.md +66 -0
  267. package/docs/oauth-authentication-guide.md +168 -0
  268. package/docs/oauth-iflow-implementation.md +153 -0
  269. package/docs/pipeline-routing-report.md +209 -0
  270. package/docs/plans/manager-daemon/PLAN.md +86 -0
  271. package/docs/plans/provider-config-v2-plan.md +176 -0
  272. package/docs/plans/provider-runtime-manager-plan.md +209 -0
  273. package/docs/plans/transparent-429-failover.md +89 -0
  274. package/docs/plans/unified-hub-framework-v1.md +245 -0
  275. package/docs/provider-config-v2-ui-design.md +181 -0
  276. package/docs/provider-quota-design.md +129 -0
  277. package/docs/providers/gemini-provider.md +62 -0
  278. package/docs/providers/lmstudio-v2-migration-report.md +102 -0
  279. package/docs/providers/provider-composite-design.md +142 -0
  280. package/docs/providers/provider-composite-testing.md +98 -0
  281. package/docs/providers/provider-type-only-migration.md +111 -0
  282. package/docs/rccx-wasm-migration.md +74 -0
  283. package/docs/refactoring/architecture-comparison-diagram.md +140 -0
  284. package/docs/refactoring/compatibility-v2-architecture-design.md +738 -0
  285. package/docs/refactoring/workflow-compatibility-refactoring-design.md +361 -0
  286. package/docs/reports/routing-classification-report.json +24 -0
  287. package/docs/reports/routing-classification-report.md +18 -0
  288. package/docs/reports/thinking-keywords-report.json +19 -0
  289. package/docs/responses/README.md +156 -0
  290. package/docs/responses-generic-provider.md +86 -0
  291. package/docs/responses-passthrough-provider-design.md +202 -0
  292. package/docs/routing-awrr-health-weighted-round-robin.md +179 -0
  293. package/docs/routing-instructions.md +393 -0
  294. package/docs/stop-message-auto.md +225 -0
  295. package/docs/streaming-flow.html +30 -0
  296. package/docs/streaming-flow.md +182 -0
  297. package/docs/token-daemon-preview.html +490 -0
  298. package/docs/token-refresh-daemon-plan.md +269 -0
  299. package/docs/transformation-tables/Gemini-FinishReason/345/256/214/346/225/264/350/275/254/346/215/242/350/241/250.json +233 -0
  300. package/docs/transformation-tables/README.md +225 -0
  301. package/docs/transformation-tables/claude-code-router-anthropic-to-gemini.json +283 -0
  302. package/docs/transformation-tables/claude-code-router-anthropic-to-openai.json +208 -0
  303. package/docs/transformation-tables/claude-code-router-openai-to-anthropic.json +261 -0
  304. package/docs/transformation-tables/claude-code-router-openai-to-gemini.json +208 -0
  305. package/docs/transformation-tables/claude-code-router-openai-to-lmstudio.json +182 -0
  306. package/docs/transformation-tables/claude-code-router-openai-to-ollama.json +250 -0
  307. package/docs/transformation-tables/claude-code-router-openai-to-textgenwebui.json +295 -0
  308. package/docs/transformation-tables/claude-code-router-provider-conversions.json +193 -0
  309. package/docs/transformation-tables//345/256/214/346/225/264/347/232/204/345/267/245/345/205/267/346/211/247/350/241/214/346/265/201/347/250/213/350/275/254/346/215/242/350/241/250.json +299 -0
  310. package/docs/transformation-tables//345/257/271/350/257/235/345/216/206/345/217/262/347/273/264/346/212/244/345/210/206/346/236/220.md +134 -0
  311. package/docs/transformation-tables//345/267/245/345/205/267/350/260/203/347/224/250/346/250/241/345/274/217/345/210/206/346/236/220.md +158 -0
  312. package/docs/transformation-tables//347/212/266/346/200/201/347/256/241/347/220/206/351/234/200/346/261/202/345/210/206/346/236/220.md +175 -0
  313. package/docs/transformation-tables//351/235/231/346/200/201/350/241/250vs/345/212/250/346/200/201/345/210/206/346/236/220.md +189 -0
  314. package/docs/transformation-tables//351/235/231/346/200/201/350/241/250/345/207/206/347/241/256/346/200/247/350/257/204/344/274/260.md +179 -0
  315. package/docs/transformation-tables//351/235/236/346/265/201/345/274/217/345/234/272/346/231/257/345/210/206/346/236/220.md +189 -0
  316. package/docs/v2-architecture/IMPLEMENTATION-ROADMAP.md +367 -0
  317. package/docs/v2-architecture/OPTIMIZED-DESIGN.md +827 -0
  318. package/docs/v2-architecture/PRERUN-CONNECTION-DESIGN.md +716 -0
  319. package/docs/v2-architecture/README.md +551 -0
  320. package/docs/verification/modelscope-verify.md +59 -0
  321. package/docs/web-search-service-design.md +322 -0
  322. package/package.json +12 -7
  323. package/scripts/camoufox/launch-auth.mjs +193 -58
  324. package/scripts/monitor-diff.mjs +126 -0
  325. package/scripts/pack-mode.mjs +19 -1
  326. package/scripts/pack-rcc.mjs +63 -0
  327. package/scripts/unified-hub-shadow-compare.mjs +33 -13
  328. package/scripts/verify-e2e-toolcall.mjs +115 -26
  329. package/dist/modules/llmswitch/pipeline-registry.d.ts +0 -57
  330. package/dist/modules/llmswitch/pipeline-registry.js +0 -229
  331. package/dist/modules/llmswitch/pipeline-registry.js.map +0 -1
  332. package/dist/server/RouteCodexServer.d.ts +0 -13
  333. package/dist/server/RouteCodexServer.js +0 -25
  334. package/dist/server/RouteCodexServer.js.map +0 -1
  335. package/dist/v2/conversion/hub/snapshot-recorder.d.ts +0 -12
  336. package/dist/v2/conversion/hub/snapshot-recorder.js +0 -22
  337. package/dist/v2/conversion/hub/snapshot-recorder.js.map +0 -1
@@ -0,0 +1,393 @@
1
+ # Routing Instructions (路由指令系统)
2
+
3
+ ## 概述
4
+
5
+ RouteCodex 支持通过用户消息中的特殊指令 `<**...**>` 来动态控制路由行为。这些指令可以在运行时修改 provider/model/key 的选择策略,而无需修改配置文件。
6
+
7
+ ## 语法格式
8
+
9
+ 指令格式为 `<**<type><target>**>`,其中:
10
+ - `<type>`:指令类型(可选 `!`、`#`、`@` 或省略)
11
+ - `<target>`:目标标识,可以是 provider、provider.model、provider.key 或 provider.key.model
12
+
13
+ ## 指令类型
14
+
15
+ ### 1. 强制指定(单次)
16
+
17
+ **语法:** `<**provider.model**>` 或 `<**provider.key.model**>`
18
+
19
+ **效果:** 仅对当前请求强制使用指定的 provider/model
20
+
21
+ **示例:**
22
+ ```
23
+ <**glm.glm-4.7**>
24
+ 请帮我写一段代码
25
+ ```
26
+
27
+ 另外,针对「整个 provider」可以使用简写:
28
+
29
+ **语法:** `<**provider**>`(无 `!`、无 `.`)
30
+
31
+ **效果:**
32
+ - 将当前会话的 provider 白名单重置为仅包含该 provider,等价于 `<**!provider**>`;
33
+ - 之后的路由只会命中当前 routing 中配置了该 provider 的池子,且池子内部只会选用该 provider 的模型/key;
34
+ - 可以与其它 sticky/禁用规则叠加使用。
35
+
36
+ **示例:**
37
+ ```text
38
+ <**antigravity**>
39
+ (当前会话仅使用 antigravity 相关的所有模型/key)
40
+ ```
41
+
42
+ ### 2. 粘性指定(持续 sticky)
43
+
44
+ **语法:**
45
+ - `<**!provider.model**>`
46
+ - `<**!provider.keyAlias.model**>`
47
+ - `<**!provider.N**>`(N 为正整数,代表第 N 个 key)
48
+
49
+ **效果(当前实现):**
50
+ - 针对当前会话(session / conversation)设置「粘性目标」,所有状态按 `stickyKey`(sessionId / conversationId / resume 链)隔离:
51
+ - `!provider.model`:锁定该 provider 下**该模型的所有 key/token**,形成一个 sticky key 池(在这个池子里轮询,默认遵循 round‑robin + 健康检查,不再被限定为某一个 token);
52
+ - `!provider.keyAlias.model` 或 `!provider.N`:锁定到指定 alias / 指定序号的**单个 key**(不会在 alias 之间轮询,只对这一个 runtimeKey 做 sticky)。
53
+ - 之后的请求(除 vision/web_search/longcontext 显式命中外)优先走「粘性路由阶段」,只使用粘性目标对应的 providerKey 集合:
54
+ - 对于 `!provider.model`:会在所有包含该 provider.model 的路由中,把这些路由里的 **所有 sticky key** 视为一个统一的候选池,并在其中轮询;
55
+ - 对于 `!provider.keyAlias.model` / `!provider.N`:直接视为对某个具体 providerKey 的硬绑定。
56
+ - 熔断与健康:
57
+ - 任意 sticky key(某个具体 token / apiKey / OAuth 实例)发生 429 / QUOTA_EXHAUSTED 或其它致命错误时,只会让**该 key** 进入熔断或失败状态,从 sticky key 池中移除;
58
+ - 只要 sticky key 池里仍有其它可用 key,就继续在这些 key 之间轮询,不会因为某一个 token 掉线就「卡死整个对话」;
59
+ - 仅当粘性目标的**所有 key** 都因健康/禁用/熔断等原因不可用时,路由器才会自动清除 sticky 状态(等价于隐式执行了一次 `clear`),后续请求恢复正常路由。
60
+
61
+ **路由优先级(有 sticky 时):**
62
+ 1. 若当前请求显式命中 `vision` / `web_search` / `longcontext`,优先尝试这些专用路由(不受 sticky 限制);
63
+ 2. 然后进入「粘性路由阶段」:在所有 **包含粘性 provider/model 的路由** 中,仅使用 sticky 对应的 providerKey 进行选择;
64
+ - 这一步会覆盖原本的 `coding` / `thinking` / `tools` / `search` 等 route_hint,实际命中的 routeName 由路由表中真正包含粘性 provider/model 的路由决定(例如你把 `antigravity.claude-sonnet-4-5-thinking` 配在 `thinking`,后续 coding/tools 请求也会命中 `thinking` 路由);
65
+ - 在这个阶段,优先级等价于:`vision / web_search / longcontext / (所有 sticky 相关路由) / default`,并且在 sticky key 池耗尽之前**不会落到 default 中的非 sticky provider**;
66
+ 3. 如果上述 sticky 阶段完全找不到可用的 provider(所有粘性 key 熔断/禁用),则自动清除 sticky,之后按正常路由表行为执行。
67
+
68
+ > **临时跳出 sticky**:Host 可在 metadata 中设置 `disableStickyRoutes: true`(仅对当前请求生效),虚拟路由会在该次路由决策前忽略 `stickyTarget`,按常规 routing 再选 provider,但不会删除原 sticky 状态。
69
+
70
+ **示例:**
71
+ ```text
72
+ <**!antigravity.claude-sonnet-4-5-thinking**>
73
+ 解决 CI 问题的话先提交 github
74
+ ```
75
+ (后续同一会话里,即便 classifier 给出 `coding` / `tools`,也会优先在实际包含 `antigravity.claude-sonnet-4-5-thinking` 的路由里选,比如命中 `thinking` 路由,并在该模型的多 token 之间轮询。)
76
+
77
+ ```text
78
+ <**!antigravity.geetasamodgeetasamoda.claude-sonnet-4-5-thinking**>
79
+ ```
80
+ (严格粘在 antigravity 的 `geetasamodgeetasamoda` 这一条 key 上,不再轮询其它 alias。)
81
+
82
+ ### 3. 允许筛选(provider 白名单)
83
+
84
+ **语法:** `<**!provider**>` 或 `<**!providerA,providerB**>`(注意:**无 `.` 时才是白名单语义**)
85
+
86
+ **效果:**
87
+ - 仅对当前会话生效;
88
+ - 将可用 provider 限制到给定集合(`allowedProviders`),所有不在白名单中的 provider 及其 key 都不会参与路由;
89
+ - 新的 `<**! ...**>` 会覆盖该会话先前设置的白名单;如需允许多个 provider,请在同一条指令中用逗号写在同一条里;
90
+ - 白名单本身不改变路由优先级,只是过滤候选 provider 集合,可以与 sticky/禁用规则叠加。
91
+
92
+ **示例:**
93
+ ```text
94
+ <**!glm**>
95
+ (当前会话只允许 glm provider 的所有 key 命中,禁用其他 provider)
96
+
97
+ <**!glm,openai**>
98
+ (当前会话只允许 glm 和 openai 两个 provider 命中,其他全部过滤)
99
+ ```
100
+
101
+ ### 4. 禁用目标
102
+
103
+ **语法:** `<**#provider**>`、`<**#provider.N**>` 或 `<**#provider.key**>`(支持用逗号声明多个禁用对象)
104
+
105
+ **效果:**
106
+ - 所有禁用状态只影响当前 session
107
+ - 新的 `<**# ...**>` 会覆盖该 session 之前的禁用列表;使用逗号可一次写入多个 provider/key
108
+ - `<**#provider**>`:禁用该 provider 的所有 key
109
+ - `<**#provider.N**>`:禁用该 provider 的第 N 个 auth key(序号从 1 开始,N 可以是任意正整数)
110
+ - `<**#provider.key**>`:禁用该 provider 的指定 keyAlias
111
+
112
+ **示例:**
113
+ ```
114
+ <**#glm**>
115
+ (禁用 glm provider 的所有 key)
116
+
117
+ <**#openai.1**>
118
+ (禁用 openai provider 的第 1 个 auth key)
119
+
120
+ <**#openai.3**>
121
+ (禁用 openai provider 的第 3 个 auth key)
122
+
123
+ <**#anthropic.primary**>
124
+ (禁用 anthropic provider 的 keyAlias 为 "primary" 的 key)
125
+
126
+ <**#glm,openai.1**>
127
+ (当前 session 禁用 glm 的所有 key,并禁用 openai 的第 1 个 key)
128
+ ```
129
+
130
+ ### 5. 启用目标
131
+
132
+ **语法:** `<**@provider**>`、`<**@provider.N**>` 或 `<**@provider.key**>`
133
+
134
+ **效果:** 解除对应目标的禁用状态
135
+
136
+ **示例:**
137
+ ```
138
+ <**@glm**>
139
+ (启用 glm provider 的所有 key)
140
+
141
+ <**@openai.1**>
142
+ (启用 openai provider 的第 1 个 auth key)
143
+
144
+ <**@openai.2**>
145
+ (启用 openai provider 的第 2 个 auth key)
146
+ ```
147
+
148
+ ### 6. 清除所有指令
149
+
150
+ **语法:** `<**clear**>`
151
+
152
+ **效果:** 清除所有强制、粘性、允许和禁用状态,恢复默认路由行为
153
+
154
+ **示例:**
155
+ ```
156
+ <**clear**>
157
+ 恢复正常路由
158
+ ```
159
+
160
+ ### 7. 自动续写 stopMessage(基于 sticky 状态)
161
+
162
+ > 仅当 RouteCodex 内置的 `stop_message_auto` servertool 启用时生效。
163
+
164
+ **语法:**
165
+
166
+ - 启用 / 更新自动续写:
167
+ - `<**stopMessage:"继续"**>` → 默认最多自动续写 1 次;
168
+ - `<**stopMessage:"继续",3**>` → 最多自动续写 3 次;
169
+ - `<**stopMessage:<file://stopMessage/message1.md>**>` → 读取 `~/.routecodex/stopMessage/message1.md` 作为 stopMessage 文案(设置时读取并缓存到内存);
170
+ - 清理 stopMessage 状态:
171
+ - `<**stopMessage:clear**>`
172
+
173
+ **行为:**
174
+
175
+ - 标签只在路由层解析,不会透传给上游模型;
176
+ - 解析后写入当前 sticky session 状态:
177
+ - `stopMessageText`:自动补发的用户消息内容;
178
+ - `stopMessageMaxRepeats`:允许自动续写的最大次数(>=1);
179
+ - `stopMessageUsed`:已执行次数(从 0 开始计数);
180
+ - 当满足以下条件时,servertool 会自动发起后续请求:
181
+ - 当前响应的 `choices[0].finish_reason === "stop"`;
182
+ - 当前轮没有工具调用(`tool_calls` 为空);
183
+ - `stopMessageUsed < stopMessageMaxRepeats`;
184
+ - 客户端仍处于连接状态(HTTP 层会在断连时设置 `clientDisconnected=true`,servertool 检测到后停止自动续写);
185
+ - 自动续写时:
186
+ - 以保存的原始 Chat 请求(`capturedChatRequest`)为基础,复制 `model/messages/tools/parameters`;
187
+ - 在消息末尾追加一条 `{ role: "user", content: stopMessageText }`;
188
+ - 自增 `stopMessageUsed` 并写回 sticky 存储;
189
+ - 通过内部 `reenterPipeline` 再发起一次 /v1/chat/completions(对客户端透明,对 Responses / Chat / Gemini 协议统一生效)。
190
+
191
+ **示例:**
192
+
193
+ ```text
194
+ <**stopMessage:"继续",3**>
195
+ 帮我把这个项目的架构分 3 步讲完,每一步结束后我会说“继续”。
196
+ ```
197
+
198
+ 在该会话中:
199
+ - 当模型先给出第 1 段回答并以 `finish_reason=stop` 结束时,服务器会自动追加一条用户消息 `继续` 并发起第 2 轮;
200
+ - 若第 2 轮仍以 `stop` 结束且客户端仍连接,则再次自动补一条 `继续` 并发起第 3 轮;
201
+ - 使用次数达到 `maxRepeats` 后自动停止,不再继续补发。
202
+
203
+ ## 目标标识格式
204
+
205
+ | 格式 | 含义 | 示例 |
206
+ |------|------|------|
207
+ | `provider` | Provider ID | `glm`, `openai`, `anthropic` |
208
+ | `provider.model` | Provider + 模型 | `glm.glm-4.7`, `openai.gpt-4` |
209
+ | `provider.key` | Provider + KeyAlias | `anthropic.primary`, `glm.backup` |
210
+ | `provider.N` | Provider + Key序号(从1开始,N为正整数) | `openai.1`, `glm.2`, `anthropic.3` |
211
+ | `provider.key.model` | 完整指定 | `openai.primary.gpt-4`, `glm.backup.glm-4.7` |
212
+
213
+ ## 优先级规则
214
+
215
+ 1. **单次强制(force)** > **粘性指定(sticky)** > **白名单(allow)** > **默认路由**
216
+ 2. **禁用规则(disable)** 在所有优先级之上(任何被禁用的目标都无法被选中)
217
+ 3. 多个指令在同一消息中时,按从左到右的顺序应用;
218
+ 4. 有 sticky 时:
219
+ - 除 `vision` / `web_search` / `longcontext` 显式命中之外,其它 route_hint(`coding` / `thinking` / `tools` / `search` 等)会被“粘性路由阶段”覆盖,优先命中真正包含粘性 provider/model 的路由;
220
+ - 当粘性 provider 的所有 key 不可用时,自动清除 sticky,再按正常路由继续。
221
+
222
+ ## 状态管理
223
+
224
+ ### 持久化
225
+
226
+ 路由指令状态按 `stickyKey` 隔离存储,`stickyKey` 的解析顺序为:
227
+ 1. 对 Responses 协议(`providerProtocol === 'openai-responses'`):
228
+ - 若存在 Responses Resume 语义:`metadata.responsesResume.previousRequestId`;
229
+ - 否则使用当前 `metadata.requestId`(仅在该条请求链路内生效)。
230
+ 2. 对其它协议:
231
+ - 优先使用 `metadata.sessionId`(如果存在);
232
+ - 否则 `metadata.conversationId`;
233
+ - 否则回退为当前 `metadata.requestId`。
234
+
235
+ 因此:
236
+ - Chat/Anthropic/Gemini 等协议下,同一 `sessionId` 或同一 `conversationId` 下的请求共享 sticky / allow / disable 状态;
237
+ - Responses 自动粘滞仅在单个 requestId/resume 链内生效,不会把 provider 选择粘到整个会话;
238
+ - 没有显式会话信息时,会退化为“按 requestId(以及 Resume 的 previousRequestId)维持的短期状态。
239
+
240
+ ### Daemon 管理
241
+
242
+ 通过 daemon 可以观察和修改当前的路由指令状态:
243
+
244
+ ```bash
245
+ # 查看当前状态
246
+ routecodex daemon status routing
247
+
248
+ # 设置全局粘性目标
249
+ routecodex daemon set routing --sticky !glm.glm-4.7
250
+
251
+ # 禁用指定 key(序号)
252
+ routecodex daemon set routing --disable openai.1
253
+
254
+ # 禁用指定 key(alias)
255
+ routecodex daemon set routing --disable anthropic.primary
256
+
257
+ # 启用指定 provider
258
+ routecodex daemon set routing --enable glm
259
+
260
+ # 清除所有指令
261
+ routecodex daemon set routing --clear
262
+
263
+ # 查看指定 server 的状态
264
+ routecodex daemon status routing --server <server-id>
265
+ ```
266
+
267
+ **Daemon 修改规则:**
268
+ - 修改后的状态需要指定影响的活动 server
269
+ - 未指定 server 时影响所有 server
270
+ - 修改立即生效,无需重启
271
+
272
+ ## 错误处理
273
+
274
+ ### 禁用错误
275
+
276
+ 当请求的目标被禁用时,返回明确的错误信息:
277
+
278
+ ```json
279
+ {
280
+ "error": "Requested provider glm is disabled",
281
+ "code": "PROVIDER_NOT_AVAILABLE",
282
+ "details": {
283
+ "provider": "glm",
284
+ "reason": "disabled"
285
+ }
286
+ }
287
+ ```
288
+
289
+ ### 不存在错误
290
+
291
+ 当请求的目标不存在于配置中时:
292
+
293
+ ```json
294
+ {
295
+ "error": "Requested provider not.found not found in provider registry",
296
+ "code": "PROVIDER_NOT_AVAILABLE",
297
+ "details": {
298
+ "provider": "not.found"
299
+ }
300
+ }
301
+ ```
302
+
303
+ ### 健康检查失败
304
+
305
+ 当请求的目标存在但健康检查失败时:
306
+
307
+ ```json
308
+ {
309
+ "error": "Requested provider glm is not available (health check failed)",
310
+ "code": "PROVIDER_NOT_AVAILABLE",
311
+ "details": {
312
+ "provider": "glm",
313
+ "reason": "unhealthy"
314
+ }
315
+ }
316
+ ```
317
+
318
+ ## 使用场景
319
+
320
+ ### 场景 1:临时切换到指定模型
321
+
322
+ ```
323
+ <**glm.glm-4.7**>
324
+ 帮我分析这段代码
325
+ ```
326
+
327
+ ### 场景 2:持续使用某个 provider
328
+
329
+ ```
330
+ <**!openai**>
331
+ (后续对话都使用 openai)
332
+ ...
333
+ <**clear**>
334
+ 恢复正常
335
+ ```
336
+
337
+ ### 场景 3:禁用某个 key 避免限流
338
+
339
+ ```
340
+ <**#openai.1**>
341
+ (禁用 openai 的第1个 key)
342
+ ```
343
+
344
+ ### 场景 4:只允许特定 provider(白名单)
345
+
346
+ ```
347
+ <**!glm**>
348
+ (只允许 glm provider)
349
+ ```
350
+
351
+ ### 场景 5:多 key 轮换场景
352
+
353
+ 配置了 3 个 openai key,遇到限流时:
354
+
355
+ ```
356
+ <**#openai.1**>
357
+ (禁用第1个 key,自动使用第2、3个)
358
+
359
+ <**#openai.2**>
360
+ (继续禁用第2个,只使用第3个)
361
+
362
+ <**@openai.1**>
363
+ (第1个 key 恢复后重新启用)
364
+ ```
365
+
366
+ ### 场景 6:按序号禁用/启用
367
+
368
+ ```
369
+ <**#anthropic.1**>
370
+ (禁用第1个 key)
371
+
372
+ <**#anthropic.2**>
373
+ (禁用第2个 key)
374
+
375
+ <**@anthropic.1**>
376
+ (启用第1个 key)
377
+ ```
378
+
379
+ ## 注意事项
380
+
381
+ 1. **消息清理**:指令标签 `<**...**>` 会被自动从用户消息中移除,不会发送给上游 AI
382
+ 2. **大小写敏感**:provider ID、keyAlias 等标识符区分大小写
383
+ 3. **序号从1开始**:`<**provider.1**>` 表示第1个 key,不是从0开始,序号可以是任意正整数
384
+ 4. **stickyKey 隔离**:不同会话的状态互不影响,使用 daemon 修改时需要指定 server
385
+ 5. **健康检查**:即使指定了目标,如果目标处于不健康状态,路由仍会失败
386
+ 6. **序号动态性**:key 序号是动态的,基于配置文件中 auth 数组的顺序
387
+
388
+ ## 参考实现
389
+
390
+ - 解析逻辑:`sharedmodule/llmswitch-core/src/router/virtual-router/routing-instructions.ts`
391
+ - 类型定义:`sharedmodule/llmswitch-core/src/router/virtual-router/types.ts`
392
+ - 集成位置:`sharedmodule/llmswitch-core/src/router/virtual-router/engine.ts`
393
+ - Daemon 接口:`src/daemon/`(待实现)
@@ -0,0 +1,225 @@
1
+ # stopMessage 自动续写设计(servertool 版)
2
+
3
+ 本设计为 `<**stopMessage:"…",N**>` / `<**stopMessage:clear**>` 语法的统一方案,基于 llmswitch-core 的虚拟路由 + servertool 机制,实现「在 finish_reason=stop 时自动追加一条用户消息并继续对话」的能力。
4
+
5
+ 目标:
6
+
7
+ - 语法与现有 `<**!glm**>` / `<**#glm**>` 一致:只在路由层解析,provider 不看到标签。
8
+ - 所有自动续写逻辑通过 **servertool** 实现,与 `vision_auto` / `gemini_empty_reply_continue` 保持一致。
9
+ - 状态通过 sticky session 持久化;支持清理与覆盖。
10
+ - 客户端真实断开(HTTP aborted/close 未写完)后不会继续自动请求,避免后台无限回环;正常完成不会被误判。
11
+
12
+ ## 1. 语法与状态
13
+
14
+ ### 1.1 语法
15
+
16
+ 支持三种形式:
17
+
18
+ - 启用 / 更新:`<**stopMessage:"继续",3**>`
19
+ - `"继续"`:自动补发的用户消息内容,内部如需引号,用 `\"` 转义。
20
+ - `3`:本会话最多自动续写 3 轮;省略时默认 `1`。
21
+ - 仅设置文案(默认 1 次):`<**stopMessage:"继续"**>` → `maxRepeats = 1`。
22
+ - 清理:`<**stopMessage:clear**>` → 清空本会话 stopMessage 状态。
23
+
24
+ > 注意:这些标签仅用于路由与 servertool,不应出现在发给 provider 的文本中。
25
+
26
+ ### 1.2 sticky 状态结构
27
+
28
+ 在虚拟路由的 sticky session state 中新增字段(由 `sticky-session-store` 维护):
29
+
30
+ - `stopMessageText?: string` — 自动续写的用户文本。
31
+ - `stopMessageMaxRepeats?: number` — 最多自动续写次数(>=1)。
32
+ - `stopMessageUsed: number` — 已执行的自动续写次数,初始为 `0`。
33
+ - `stopMessageUpdatedAt?: number` — 最近一次解析 `<**stopMessage:"..."**>` 指令的时间戳。
34
+ - `stopMessageLastUsedAt?: number` — 最近一次 servertool 自动补发 stopMessage 的时间戳。
35
+
36
+ 行为:
37
+
38
+ - 解析到 `stopMessage:"...",N`:
39
+ - `stopMessageText = text`
40
+ - `stopMessageMaxRepeats = N || 1`
41
+ - `stopMessageUsed = 0`
42
+ - `stopMessageUpdatedAt = Date.now()`,`stopMessageLastUsedAt = undefined`
43
+ - 解析到 `stopMessage:clear`:
44
+ - 删除上述所有字段。
45
+ - **会话标识要求**
46
+ - sticky state 依赖 `sessionId` / `conversationId`。`/v1/chat` 与 `/v1/responses` 默认通过请求头即可提供;`/v1/messages` 需要确保请求体中的 `metadata.user_id` 含有 `session_<uuid>` 片段(Claude/Antigravity 默认行为)。llmswitch-core 会在缺少 header/metadata.sessionId 时回退解析 `metadata.__raw_request_body.metadata.user_id`,自动恢复 sessionId 并写入 sticky 文件。若缺失上述字段,stopMessage 无法跨请求记忆次数。
47
+
48
+ ## 2. 路由层解析与指令清理
49
+
50
+ ### 2.1 解析入口
51
+
52
+ 在 `llmswitch-core/src/router/virtual-router/routing-instructions.ts` 中扩展现有 `<**...**>` 解析流程:
53
+
54
+ 1. 扫描 user 消息,从最后一条带 `<**...**>` 的 user 消息中提取指令字符串(复用现有逻辑)。
55
+ 2. 对每个 `<**...**>` token:
56
+ - 若前缀为 `stopMessage:`,进入 stopMessage 解析分支:
57
+ - `stopMessage:"...",N`
58
+ - `stopMessage:"..."`
59
+ - `stopMessage:clear`
60
+ - 解析结果不进入 provider 路由选择,而是写入 sticky session state。
61
+
62
+ ### 2.2 消息清理
63
+
64
+ 为确保 provider 完全看不到控制标签,在标准化 Chat 请求前做统一清理:
65
+
66
+ - 在 chat inbound 处理链路中(Chat → StandardizedMessage 期间),对 user 文本执行:
67
+ - 用正则移除所有 `<**...**>` 片段(包括 `!glm` / `#glm` / `stopMessage` 等)。
68
+ - 保持其余文本内容不变。
69
+
70
+ 这样可保证:
71
+
72
+ - 路由与工具治理仍能看到完整指令。
73
+ - provider 收到的 `messages[].content` 不含任何 `<**...**>` 控制标记。
74
+
75
+ ## 3. servertool:stop_message_auto
76
+
77
+ ### 3.1 位置与注册
78
+
79
+ - 新增文件:`llmswitch-core/src/servertool/handlers/stop-message-auto.ts`。
80
+ - 在 `server-side-tools.ts` 中 import 并注册:
81
+
82
+ ```ts
83
+ import './handlers/stop-message-auto.js';
84
+ // ...
85
+ registerServerToolHandler('stop_message_auto', handler, { trigger: 'auto' });
86
+ ```
87
+
88
+ ### 3.2 触发条件
89
+
90
+ handler 签名:`ServerToolHandler`,在 servertool orchestrator 中自动触发。伪代码:
91
+
92
+ ```ts
93
+ const handler: ServerToolHandler = async (ctx) => {
94
+ if (!ctx.options.reenterPipeline) return null;
95
+
96
+ // 避免在 followup 请求中再次触发(防循环)
97
+ const record = ctx.adapterContext as {
98
+ serverToolFollowup?: unknown;
99
+ clientDisconnected?: unknown;
100
+ clientConnectionState?: { disconnected?: boolean };
101
+ };
102
+ if (record.serverToolFollowup === true || String(record.serverToolFollowup).toLowerCase() === 'true') {
103
+ return null;
104
+ }
105
+
106
+ // 客户端已断开:不再自动续写,避免后台死循环
107
+ if (record.clientConnectionState?.disconnected === true ||
108
+ record.clientDisconnected === true ||
109
+ String(record.clientDisconnected).toLowerCase() === 'true') {
110
+ return null;
111
+ }
112
+
113
+ // 从 sticky session state 读取 stopMessage 状态(通过已有 helper)
114
+ const state = readStickySessionState(ctx.adapterContext);
115
+ if (!state?.stopMessageText || !state.stopMessageMaxRepeats) return null;
116
+ if (state.stopMessageUsed >= state.stopMessageMaxRepeats) return null;
117
+
118
+ // 检查当前响应 finish_reason
119
+ if (!isStopFinishReason(ctx.base)) return null;
120
+
121
+ // 更新计数并持久化
122
+ state.stopMessageUsed += 1;
123
+ state.stopMessageLastUsedAt = Date.now();
124
+ writeStickySessionState(ctx.adapterContext, state);
125
+
126
+ const captured = getCapturedRequest(ctx.adapterContext);
127
+ if (!captured) return null;
128
+ const followupPayload = buildStopMessageFollowupPayload(captured, state.stopMessageText);
129
+ if (!followupPayload) return null;
130
+
131
+ return {
132
+ chatResponse: ctx.base,
133
+ execution: {
134
+ flowId: 'stop_message_flow',
135
+ followup: {
136
+ requestIdSuffix: ':stop_followup',
137
+ payload: followupPayload,
138
+ metadata: {
139
+ serverToolFollowup: true,
140
+ stream: false,
141
+ preserveRouteHint: false,
142
+ disableStickyRoutes: true,
143
+ clientConnectionState: record.clientConnectionState
144
+ }
145
+ }
146
+ }
147
+ };
148
+ };
149
+ ```
150
+
151
+ 要点:
152
+
153
+ - 不关心入口 endpoint(`/v1/responses`、`/v1/chat/completions` 等),由 servertool orchestrator 统一挂载。
154
+ - 是否继续完全由:
155
+ - sticky state(有无 stopMessage、次数是否已用完);
156
+ - 当前响应 finish_reason 是否为 `"stop"`;
157
+ - client 是否 still connected;
158
+ 三者共同决定。
159
+ - followup 请求默认沿用最初的入口 endpoint(`/v1/chat/completions`、`/v1/responses` 等),从而复用相同的 Virtual Router 路径与工具治理;如需特殊入口可在 handler 的 `followup.entryEndpoint` 中显式指定。
160
+
161
+ ### 3.3 finish_reason 判断
162
+
163
+ 实现辅助函数 `isStopFinishReason(base: JsonObject): boolean`:
164
+
165
+ - 对 OpenAI Chat 形状:
166
+ - `base.choices[0].finish_reason === 'stop'`。
167
+ - 对 OpenAI Responses 形状(如需要):
168
+ - 映射到内部标准字段后,检查对应 finish_reason 是否为 `stop`。
169
+
170
+ 为避免过度耦合,可先只在 Chat 路径上启用,Responses 配置另行评估。
171
+
172
+ ### 3.4 followup payload 构造
173
+
174
+ 与 vision / gemini-continue 相同思路:
175
+
176
+ ```ts
177
+ function getCapturedRequest(adapterContext: unknown): JsonObject | null {
178
+ const ctx = adapterContext as { capturedChatRequest?: unknown };
179
+ const captured = ctx?.capturedChatRequest;
180
+ if (!captured || typeof captured !== 'object' || Array.isArray(captured)) return null;
181
+ return captured as JsonObject;
182
+ }
183
+
184
+ function buildStopMessageFollowupPayload(source: JsonObject, text: string): JsonObject | null {
185
+ if (!source || typeof source !== 'object') return null;
186
+ const payload: Record<string, unknown> = {};
187
+ if (typeof source.model === 'string' && source.model.trim()) {
188
+ payload.model = source.model.trim();
189
+ }
190
+ const rawMessages = (source as { messages?: unknown }).messages;
191
+ const messages = Array.isArray(rawMessages) ? cloneJson(rawMessages) : [];
192
+ messages.push({ role: 'user', content: text } as JsonObject);
193
+ payload.messages = messages;
194
+
195
+ if (Array.isArray((source as { tools?: unknown }).tools)) {
196
+ payload.tools = cloneJson((source as { tools: unknown[] }).tools);
197
+ }
198
+ const parameters = (source as { parameters?: unknown }).parameters;
199
+ if (parameters && typeof parameters === 'object' && !Array.isArray(parameters)) {
200
+ Object.assign(payload, cloneJson(parameters as Record<string, unknown>));
201
+ }
202
+ return payload as JsonObject;
203
+ }
204
+ ```
205
+
206
+ ## 4. 行为对齐检查
207
+
208
+ 对照需求逐条确认:
209
+
210
+ 1. **`<**stopMessage:"",3**>` 语法**
211
+ - 已支持字符串 + 次数;解析后写入 sticky state,provider 看不到标签。
212
+ 2. **`<**stopMessage:clear**>` 清理状态**
213
+ - 在 routing 指令解析阶段将三项字段全部删除,后续 servertool 将不再触发。
214
+ 3. **状态持久化**
215
+ - 依赖 sticky session store;状态挂在 session state 上,通过 `conversation_id` / `session_id` 恢复,可跨进程保持,同时记录 `stopMessageUpdatedAt/stopMessageLastUsedAt` 便于排查。
216
+ 4. **不设额外全局上限**
217
+ - servertool 只检查 `stopMessageUsed < stopMessageMaxRepeats`,次数由指令数字完全决定,不再额外 clamp。
218
+ 5. **客户端断开后停止**
219
+ - HTTP server 为每个请求追踪 `clientConnectionState.disconnected`,servertool 与 orchestrator 在该 flag 为 true 时立即终止,避免错误追加;
220
+ - 正常返回(`res.writableFinished === true`)不会触发断线判定。
221
+ 6. **与 gemini 自动继续的统一性**
222
+ - 同样通过 servertool (`*_auto` handler) + `reenterPipeline` 实现;
223
+ - 行为统一:从 `capturedChatRequest` 构造 followup payload,在 Virtual Router 入口再次进入 pipeline。
224
+
225
+ 该设计仅约束 llmswitch-core 的行为,Host / Provider 层不需要感知 stopMessage 语义,符合「工具与路由逻辑统一放在 core」的约定。*** End Patch***Eassistant to=functions.apply_patch_ENTRIES_JSON娱乐主管 to=functions.apply_patch_TYPING_JSON 재assistant통령 to=functions.apply_patch VerifiedJson Input Correction to=functions.apply_patch բուժ to=functions.apply_patch ***!
@@ -0,0 +1,30 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <title>/v1/messages Streaming Flow</title>
6
+ <script src="https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js"></script>
7
+ <script>mermaid.initialize({ startOnLoad: true, theme: 'default' });</script>
8
+ <style>
9
+ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; margin: 24px; }
10
+ pre { background: #f5f5f5; padding: 16px; overflow: auto; }
11
+ </style>
12
+ </head>
13
+ <body>
14
+ <h1>/v1/messages Streaming Pipeline</h1>
15
+ <pre class="mermaid">
16
+ flowchart TD
17
+ A["Client Request\n(JSON or SSE body)\nfields: messages/tools/tool_choice/stream"] --> B
18
+ B["HTTP Handler\nsrc/server/handlers/messages-handler.ts\nstream := body.stream\nmetadata.stream/inbound/outbound := stream"] --> C
19
+ C["req_inbound_stage1/2\nChatEnvelope parameters.stream := stream\ncollect tools/tool_outputs/tool_choice"] --> D
20
+ D["req_process_stage1_tool_governance\nTool filters adjust tool list/tool_choice\nstream untouched"] --> E
21
+ E["req_outbound_stage1/2\nBuild provider payload\npayload.stream := override(supportsStreaming) or stream"] --> F
22
+ F["Provider Upstream\nSSE or JSON depending on payload.stream\nreturns SSE/JSON with tool_calls & finish_reason"] --> G
23
+ G["resp_inbound stages\nDecode SSE, map to Chat response\ncapture tool_calls/tool outputs"] --> H
24
+ H["resp_process stages\nTool governance validation\nBuild final Chat response"] --> I
25
+ I["resp_outbound_stage1\nMap Chat response to client protocol\n(Anthropic/OpenAI/Responses)"] --> J
26
+ J["resp_outbound_stage2\nif metadata.stream == true -> emit SSE\nelse -> JSON"] --> K
27
+ K["HTTP Response\nsendPipelineResponse\nforceSSE := metadata.stream"]
28
+ </pre>
29
+ </body>
30
+ </html>