@jsonstudio/rcc 0.89.168 → 0.89.524

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (431) hide show
  1. package/README.md +18 -15
  2. package/dist/build-info.js +3 -3
  3. package/dist/build-info.js.map +1 -1
  4. package/dist/cli.js +94 -0
  5. package/dist/cli.js.map +1 -1
  6. package/dist/client/gemini-cli/gemini-cli-protocol-client.js +28 -5
  7. package/dist/client/gemini-cli/gemini-cli-protocol-client.js.map +1 -1
  8. package/dist/commands/token-daemon.d.ts +2 -0
  9. package/dist/commands/token-daemon.js +183 -0
  10. package/dist/commands/token-daemon.js.map +1 -0
  11. package/dist/error-handling/quiet-error-handling-center.d.ts +9 -0
  12. package/dist/error-handling/quiet-error-handling-center.js +141 -0
  13. package/dist/error-handling/quiet-error-handling-center.js.map +1 -0
  14. package/dist/error-handling/route-error-hub.js +8 -2
  15. package/dist/error-handling/route-error-hub.js.map +1 -1
  16. package/dist/index.js +4 -3
  17. package/dist/index.js.map +1 -1
  18. package/dist/modules/llmswitch/bridge.d.ts +1 -1
  19. package/dist/modules/llmswitch/bridge.js +3 -2
  20. package/dist/modules/llmswitch/bridge.js.map +1 -1
  21. package/dist/modules/pipeline/utils/colored-logger.d.ts +2 -0
  22. package/dist/modules/pipeline/utils/colored-logger.js +22 -3
  23. package/dist/modules/pipeline/utils/colored-logger.js.map +1 -1
  24. package/dist/providers/auth/antigravity-userinfo-helper.d.ts +10 -0
  25. package/dist/providers/auth/antigravity-userinfo-helper.js +140 -0
  26. package/dist/providers/auth/antigravity-userinfo-helper.js.map +1 -0
  27. package/dist/providers/auth/gemini-cli-userinfo-helper.js +12 -2
  28. package/dist/providers/auth/gemini-cli-userinfo-helper.js.map +1 -1
  29. package/dist/providers/auth/oauth-lifecycle.js +395 -24
  30. package/dist/providers/auth/oauth-lifecycle.js.map +1 -1
  31. package/dist/providers/auth/token-scanner/index.d.ts +32 -0
  32. package/dist/providers/auth/token-scanner/index.js +86 -0
  33. package/dist/providers/auth/token-scanner/index.js.map +1 -0
  34. package/dist/providers/auth/tokenfile-auth.d.ts +17 -0
  35. package/dist/providers/auth/tokenfile-auth.js +27 -5
  36. package/dist/providers/auth/tokenfile-auth.js.map +1 -1
  37. package/dist/providers/core/api/provider-types.d.ts +10 -0
  38. package/dist/providers/core/config/oauth-flows.d.ts +25 -0
  39. package/dist/providers/core/config/oauth-flows.js +92 -5
  40. package/dist/providers/core/config/oauth-flows.js.map +1 -1
  41. package/dist/providers/core/config/provider-oauth-configs.js +93 -2
  42. package/dist/providers/core/config/provider-oauth-configs.js.map +1 -1
  43. package/dist/providers/core/config/service-profiles.js +18 -10
  44. package/dist/providers/core/config/service-profiles.js.map +1 -1
  45. package/dist/providers/core/runtime/base-provider.d.ts +2 -0
  46. package/dist/providers/core/runtime/base-provider.js +135 -15
  47. package/dist/providers/core/runtime/base-provider.js.map +1 -1
  48. package/dist/providers/core/runtime/gemini-cli-http-provider.d.ts +8 -3
  49. package/dist/providers/core/runtime/gemini-cli-http-provider.js +332 -67
  50. package/dist/providers/core/runtime/gemini-cli-http-provider.js.map +1 -1
  51. package/dist/providers/core/runtime/http-request-executor.d.ts +1 -0
  52. package/dist/providers/core/runtime/http-request-executor.js +41 -1
  53. package/dist/providers/core/runtime/http-request-executor.js.map +1 -1
  54. package/dist/providers/core/runtime/http-transport-provider.d.ts +27 -0
  55. package/dist/providers/core/runtime/http-transport-provider.js +342 -69
  56. package/dist/providers/core/runtime/http-transport-provider.js.map +1 -1
  57. package/dist/providers/core/runtime/provider-error-classifier.d.ts +2 -2
  58. package/dist/providers/core/runtime/provider-error-classifier.js +14 -4
  59. package/dist/providers/core/runtime/provider-error-classifier.js.map +1 -1
  60. package/dist/providers/core/runtime/provider-factory.d.ts +1 -0
  61. package/dist/providers/core/runtime/provider-factory.js +37 -8
  62. package/dist/providers/core/runtime/provider-factory.js.map +1 -1
  63. package/dist/providers/core/runtime/responses-provider.d.ts +3 -3
  64. package/dist/providers/core/runtime/responses-provider.js +56 -117
  65. package/dist/providers/core/runtime/responses-provider.js.map +1 -1
  66. package/dist/providers/core/runtime/vision-debug-utils.d.ts +13 -0
  67. package/dist/providers/core/runtime/vision-debug-utils.js +114 -0
  68. package/dist/providers/core/runtime/vision-debug-utils.js.map +1 -0
  69. package/dist/providers/core/strategies/oauth-auth-code-flow.js +82 -25
  70. package/dist/providers/core/strategies/oauth-auth-code-flow.js.map +1 -1
  71. package/dist/providers/core/utils/http-client.d.ts +5 -0
  72. package/dist/providers/core/utils/http-client.js +31 -4
  73. package/dist/providers/core/utils/http-client.js.map +1 -1
  74. package/dist/providers/core/utils/provider-error-reporter.js +8 -2
  75. package/dist/providers/core/utils/provider-error-reporter.js.map +1 -1
  76. package/dist/providers/core/utils/snapshot-writer.d.ts +1 -1
  77. package/dist/providers/core/utils/snapshot-writer.js +5 -1
  78. package/dist/providers/core/utils/snapshot-writer.js.map +1 -1
  79. package/dist/providers/profile/provider-profile-loader.js +8 -4
  80. package/dist/providers/profile/provider-profile-loader.js.map +1 -1
  81. package/dist/runtime/runtime-flags.d.ts +4 -0
  82. package/dist/runtime/runtime-flags.js +32 -0
  83. package/dist/runtime/runtime-flags.js.map +1 -0
  84. package/dist/server/handlers/handler-utils.js +29 -2
  85. package/dist/server/handlers/handler-utils.js.map +1 -1
  86. package/dist/server/handlers/messages-handler.js +27 -26
  87. package/dist/server/handlers/messages-handler.js.map +1 -1
  88. package/dist/server/handlers/responses-handler.js +35 -1
  89. package/dist/server/handlers/responses-handler.js.map +1 -1
  90. package/dist/server/handlers/sse-dispatcher.js +22 -2
  91. package/dist/server/handlers/sse-dispatcher.js.map +1 -1
  92. package/dist/server/runtime/http-server/index.d.ts +10 -0
  93. package/dist/server/runtime/http-server/index.js +551 -148
  94. package/dist/server/runtime/http-server/index.js.map +1 -1
  95. package/dist/server/runtime/http-server/request-executor.d.ts +14 -0
  96. package/dist/server/runtime/http-server/request-executor.js +638 -149
  97. package/dist/server/runtime/http-server/request-executor.js.map +1 -1
  98. package/dist/server/runtime/http-server/routes.d.ts +5 -0
  99. package/dist/server/runtime/http-server/routes.js +69 -0
  100. package/dist/server/runtime/http-server/routes.js.map +1 -1
  101. package/dist/server/runtime/http-server/runtime-manager.js +18 -0
  102. package/dist/server/runtime/http-server/runtime-manager.js.map +1 -1
  103. package/dist/server/utils/sse-request-parser.d.ts +1 -0
  104. package/dist/server/utils/sse-request-parser.js +17 -6
  105. package/dist/server/utils/sse-request-parser.js.map +1 -1
  106. package/dist/server/utils/utf8-chunk-buffer.d.ts +43 -0
  107. package/dist/server/utils/utf8-chunk-buffer.js +132 -0
  108. package/dist/server/utils/utf8-chunk-buffer.js.map +1 -0
  109. package/dist/server/utils/warmup-detector.d.ts +7 -0
  110. package/dist/server/utils/warmup-detector.js +125 -0
  111. package/dist/server/utils/warmup-detector.js.map +1 -0
  112. package/dist/server/utils/warmup-storm-tracker.d.ts +9 -0
  113. package/dist/server/utils/warmup-storm-tracker.js +61 -0
  114. package/dist/server/utils/warmup-storm-tracker.js.map +1 -0
  115. package/dist/token-daemon/index.d.ts +7 -0
  116. package/dist/token-daemon/index.js +242 -0
  117. package/dist/token-daemon/index.js.map +1 -0
  118. package/dist/token-daemon/server-utils.d.ts +33 -0
  119. package/dist/token-daemon/server-utils.js +155 -0
  120. package/dist/token-daemon/server-utils.js.map +1 -0
  121. package/dist/token-daemon/token-daemon.d.ts +20 -0
  122. package/dist/token-daemon/token-daemon.js +144 -0
  123. package/dist/token-daemon/token-daemon.js.map +1 -0
  124. package/dist/token-daemon/token-types.d.ts +44 -0
  125. package/dist/token-daemon/token-types.js +18 -0
  126. package/dist/token-daemon/token-types.js.map +1 -0
  127. package/dist/token-daemon/token-utils.d.ts +17 -0
  128. package/dist/token-daemon/token-utils.js +153 -0
  129. package/dist/token-daemon/token-utils.js.map +1 -0
  130. package/dist/tools/semantic-replay.js +7 -6
  131. package/dist/tools/semantic-replay.js.map +1 -1
  132. package/dist/utils/debug-utils.js +14 -0
  133. package/dist/utils/debug-utils.js.map +1 -1
  134. package/dist/utils/error-handler-registry.d.ts +36 -0
  135. package/dist/utils/error-handler-registry.js +99 -12
  136. package/dist/utils/error-handler-registry.js.map +1 -1
  137. package/dist/utils/error-handling-utils.js +4 -3
  138. package/dist/utils/error-handling-utils.js.map +1 -1
  139. package/dist/utils/log-helpers.d.ts +6 -0
  140. package/dist/utils/log-helpers.js +90 -0
  141. package/dist/utils/log-helpers.js.map +1 -0
  142. package/dist/utils/logger.d.ts +8 -0
  143. package/dist/utils/logger.js +55 -2
  144. package/dist/utils/logger.js.map +1 -1
  145. package/dist/utils/snapshot-writer.js +2 -6
  146. package/dist/utils/snapshot-writer.js.map +1 -1
  147. package/node_modules/@jsonstudio/llms/README.md +2 -0
  148. package/node_modules/@jsonstudio/llms/dist/conversion/codecs/gemini-openai-codec.js +152 -6
  149. package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/auto-thinking.d.ts +6 -0
  150. package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/auto-thinking.js +25 -0
  151. package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/field-mapping.d.ts +14 -0
  152. package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/field-mapping.js +155 -0
  153. package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/gemini-web-search.d.ts +17 -0
  154. package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/gemini-web-search.js +68 -0
  155. package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/glm-image-content.d.ts +2 -0
  156. package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/glm-image-content.js +83 -0
  157. package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/glm-tool-extraction.d.ts +2 -0
  158. package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/glm-tool-extraction.js +264 -0
  159. package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/glm-vision-prompt.d.ts +11 -0
  160. package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/glm-vision-prompt.js +177 -0
  161. package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/glm-web-search.d.ts +2 -0
  162. package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/glm-web-search.js +63 -0
  163. package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/qwen-transform.d.ts +3 -0
  164. package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/qwen-transform.js +209 -0
  165. package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/request-rules.d.ts +24 -0
  166. package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/request-rules.js +63 -0
  167. package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/response-blacklist.d.ts +14 -0
  168. package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/response-blacklist.js +85 -0
  169. package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/response-normalize.d.ts +5 -0
  170. package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/response-normalize.js +121 -0
  171. package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/response-validate.d.ts +5 -0
  172. package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/response-validate.js +76 -0
  173. package/{dist/providers/compat/utils/snapshot-writer.d.ts → node_modules/@jsonstudio/llms/dist/conversion/compat/actions/snapshot.d.ts} +2 -2
  174. package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/snapshot.js +21 -0
  175. package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/tool-schema.d.ts +6 -0
  176. package/{dist/providers/compat/glm/utils/tool-schema-helpers.js → node_modules/@jsonstudio/llms/dist/conversion/compat/actions/tool-schema.js} +6 -1
  177. package/{dist/providers/compat/filters → node_modules/@jsonstudio/llms/dist/conversion/compat/actions}/universal-shape-filter.d.ts +17 -22
  178. package/{dist/providers/compat/filters → node_modules/@jsonstudio/llms/dist/conversion/compat/actions}/universal-shape-filter.js +46 -99
  179. package/node_modules/@jsonstudio/llms/dist/conversion/compat/profiles/chat-gemini.json +17 -0
  180. package/node_modules/@jsonstudio/llms/dist/conversion/compat/profiles/chat-glm.json +196 -13
  181. package/node_modules/@jsonstudio/llms/dist/conversion/compat/profiles/chat-iflow.json +194 -26
  182. package/node_modules/@jsonstudio/llms/dist/conversion/compat/profiles/chat-lmstudio.json +43 -35
  183. package/node_modules/@jsonstudio/llms/dist/conversion/compat/profiles/chat-qwen.json +20 -16
  184. package/node_modules/@jsonstudio/llms/dist/conversion/compat/profiles/responses-c4m.json +42 -42
  185. package/node_modules/@jsonstudio/llms/dist/conversion/config/sample-config.json +1 -1
  186. package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/compat/compat-engine.d.ts +7 -2
  187. package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/compat/compat-engine.js +5 -665
  188. package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/compat/compat-pipeline-executor.d.ts +9 -0
  189. package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/compat/compat-pipeline-executor.js +869 -0
  190. package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/compat/compat-types.d.ts +55 -0
  191. package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/hub-pipeline.d.ts +2 -0
  192. package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/hub-pipeline.js +74 -5
  193. package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage3_compat/index.js +2 -2
  194. package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/target-utils.js +9 -0
  195. package/node_modules/@jsonstudio/llms/dist/conversion/hub/process/chat-process.js +213 -1
  196. package/node_modules/@jsonstudio/llms/dist/conversion/hub/response/provider-response.d.ts +34 -0
  197. package/node_modules/@jsonstudio/llms/dist/conversion/hub/response/provider-response.js +84 -24
  198. package/node_modules/@jsonstudio/llms/dist/conversion/hub/response/response-runtime.js +19 -2
  199. package/node_modules/@jsonstudio/llms/dist/conversion/hub/response/server-side-tools.d.ts +26 -0
  200. package/node_modules/@jsonstudio/llms/dist/conversion/hub/response/server-side-tools.js +383 -0
  201. package/node_modules/@jsonstudio/llms/dist/conversion/hub/semantic-mappers/gemini-mapper.js +241 -14
  202. package/node_modules/@jsonstudio/llms/dist/conversion/hub/semantic-mappers/responses-mapper.js +17 -1
  203. package/node_modules/@jsonstudio/llms/dist/conversion/hub/standardized-bridge.js +14 -0
  204. package/node_modules/@jsonstudio/llms/dist/conversion/hub/types/standardized.d.ts +1 -0
  205. package/node_modules/@jsonstudio/llms/dist/conversion/responses/responses-host-policy.d.ts +6 -0
  206. package/node_modules/@jsonstudio/llms/dist/conversion/responses/responses-host-policy.js +14 -0
  207. package/node_modules/@jsonstudio/llms/dist/conversion/responses/responses-openai-bridge.js +133 -5
  208. package/node_modules/@jsonstudio/llms/dist/conversion/shared/anthropic-message-utils.js +98 -3
  209. package/node_modules/@jsonstudio/llms/dist/conversion/shared/bridge-message-utils.js +137 -10
  210. package/node_modules/@jsonstudio/llms/dist/conversion/shared/responses-output-builder.js +43 -2
  211. package/node_modules/@jsonstudio/llms/dist/conversion/shared/responses-reasoning-registry.d.ts +4 -0
  212. package/node_modules/@jsonstudio/llms/dist/conversion/shared/responses-reasoning-registry.js +62 -1
  213. package/node_modules/@jsonstudio/llms/dist/conversion/shared/responses-response-utils.js +23 -1
  214. package/node_modules/@jsonstudio/llms/dist/conversion/shared/snapshot-utils.js +17 -47
  215. package/node_modules/@jsonstudio/llms/dist/conversion/shared/tool-canonicalizer.d.ts +2 -0
  216. package/node_modules/@jsonstudio/llms/dist/conversion/shared/tool-filter-pipeline.js +12 -0
  217. package/node_modules/@jsonstudio/llms/dist/conversion/shared/tool-mapping.js +25 -2
  218. package/node_modules/@jsonstudio/llms/dist/index.d.ts +1 -0
  219. package/node_modules/@jsonstudio/llms/dist/index.js +1 -0
  220. package/node_modules/@jsonstudio/llms/dist/router/virtual-router/bootstrap.js +540 -36
  221. package/node_modules/@jsonstudio/llms/dist/router/virtual-router/classifier.js +12 -11
  222. package/node_modules/@jsonstudio/llms/dist/router/virtual-router/context-advisor.d.ts +19 -0
  223. package/node_modules/@jsonstudio/llms/dist/router/virtual-router/context-advisor.js +64 -0
  224. package/node_modules/@jsonstudio/llms/dist/router/virtual-router/engine.d.ts +26 -0
  225. package/node_modules/@jsonstudio/llms/dist/router/virtual-router/engine.js +450 -54
  226. package/node_modules/@jsonstudio/llms/dist/router/virtual-router/features.js +23 -458
  227. package/node_modules/@jsonstudio/llms/dist/router/virtual-router/health-manager.js +2 -7
  228. package/node_modules/@jsonstudio/llms/dist/router/virtual-router/message-utils.d.ts +7 -0
  229. package/node_modules/@jsonstudio/llms/dist/router/virtual-router/message-utils.js +78 -0
  230. package/node_modules/@jsonstudio/llms/dist/router/virtual-router/provider-registry.js +7 -2
  231. package/node_modules/@jsonstudio/llms/dist/router/virtual-router/token-counter.js +14 -3
  232. package/node_modules/@jsonstudio/llms/dist/router/virtual-router/token-estimator.d.ts +2 -0
  233. package/node_modules/@jsonstudio/llms/dist/router/virtual-router/token-estimator.js +16 -0
  234. package/node_modules/@jsonstudio/llms/dist/router/virtual-router/token-file-scanner.d.ts +15 -0
  235. package/node_modules/@jsonstudio/llms/dist/router/virtual-router/token-file-scanner.js +56 -0
  236. package/node_modules/@jsonstudio/llms/dist/router/virtual-router/tool-signals.d.ts +13 -0
  237. package/node_modules/@jsonstudio/llms/dist/router/virtual-router/tool-signals.js +403 -0
  238. package/node_modules/@jsonstudio/llms/dist/router/virtual-router/types.d.ts +86 -2
  239. package/node_modules/@jsonstudio/llms/dist/router/virtual-router/types.js +3 -1
  240. package/node_modules/@jsonstudio/llms/dist/servertool/engine.d.ts +27 -0
  241. package/node_modules/@jsonstudio/llms/dist/servertool/engine.js +60 -0
  242. package/node_modules/@jsonstudio/llms/dist/servertool/flow-types.d.ts +40 -0
  243. package/node_modules/@jsonstudio/llms/dist/servertool/flow-types.js +1 -0
  244. package/node_modules/@jsonstudio/llms/dist/servertool/handlers/vision.d.ts +1 -0
  245. package/node_modules/@jsonstudio/llms/dist/servertool/handlers/vision.js +194 -0
  246. package/node_modules/@jsonstudio/llms/dist/servertool/handlers/web-search.d.ts +1 -0
  247. package/node_modules/@jsonstudio/llms/dist/servertool/handlers/web-search.js +638 -0
  248. package/node_modules/@jsonstudio/llms/dist/servertool/orchestration-types.d.ts +33 -0
  249. package/node_modules/@jsonstudio/llms/dist/servertool/orchestration-types.js +1 -0
  250. package/node_modules/@jsonstudio/llms/dist/servertool/registry.d.ts +18 -0
  251. package/node_modules/@jsonstudio/llms/dist/servertool/registry.js +27 -0
  252. package/node_modules/@jsonstudio/llms/dist/servertool/server-side-tools.d.ts +8 -0
  253. package/node_modules/@jsonstudio/llms/dist/servertool/server-side-tools.js +208 -0
  254. package/node_modules/@jsonstudio/llms/dist/servertool/types.d.ts +88 -0
  255. package/node_modules/@jsonstudio/llms/dist/servertool/types.js +1 -0
  256. package/node_modules/@jsonstudio/llms/dist/servertool/vision-tool.d.ts +2 -0
  257. package/node_modules/@jsonstudio/llms/dist/servertool/vision-tool.js +185 -0
  258. package/node_modules/@jsonstudio/llms/dist/sse/json-to-sse/event-generators/responses.js +15 -3
  259. package/node_modules/@jsonstudio/llms/dist/sse/sse-to-json/builders/response-builder.js +6 -3
  260. package/node_modules/@jsonstudio/llms/dist/sse/sse-to-json/gemini-sse-to-json-converter.js +27 -1
  261. package/node_modules/@jsonstudio/llms/dist/sse/types/gemini-types.d.ts +20 -1
  262. package/node_modules/@jsonstudio/llms/dist/sse/types/responses-types.js +1 -1
  263. package/node_modules/@jsonstudio/llms/dist/telemetry/stats-center.d.ts +73 -0
  264. package/node_modules/@jsonstudio/llms/dist/telemetry/stats-center.js +280 -0
  265. package/node_modules/@jsonstudio/llms/package.json +2 -2
  266. package/package.json +11 -10
  267. package/scripts/README.md +26 -12
  268. package/scripts/auth-antigravity-token.mjs +64 -0
  269. package/scripts/auth-gemini-cli-token.mjs +96 -0
  270. package/scripts/auth-iflow-manual.mjs +81 -0
  271. package/scripts/auth-iflow-token-direct.mjs +87 -0
  272. package/scripts/auth-iflow-token.mjs +77 -0
  273. package/scripts/copy-compat-assets.mjs +3 -15
  274. package/scripts/install-verify.mjs +1 -0
  275. package/scripts/pack-mode.mjs +2 -1
  276. package/scripts/publish-rcc.mjs +20 -4
  277. package/scripts/replay-codex-sample.mjs +13 -8
  278. package/scripts/tests/chat-pipeline-blackbox.mjs +1 -1
  279. package/scripts/tests/virtual-router-health.mjs +141 -6
  280. package/scripts/tools/capture-provider-goldens.mjs +8 -7
  281. package/scripts/verify-client-headers.mjs +224 -0
  282. package/dist/providers/compat/base-compatibility.d.ts +0 -27
  283. package/dist/providers/compat/base-compatibility.js +0 -143
  284. package/dist/providers/compat/base-compatibility.js.map +0 -1
  285. package/dist/providers/compat/compat-directory-loader.d.ts +0 -4
  286. package/dist/providers/compat/compat-directory-loader.js +0 -85
  287. package/dist/providers/compat/compat-directory-loader.js.map +0 -1
  288. package/dist/providers/compat/compatibility-adapter.d.ts +0 -18
  289. package/dist/providers/compat/compatibility-adapter.js +0 -104
  290. package/dist/providers/compat/compatibility-adapter.js.map +0 -1
  291. package/dist/providers/compat/compatibility-factory.d.ts +0 -57
  292. package/dist/providers/compat/compatibility-factory.js +0 -155
  293. package/dist/providers/compat/compatibility-factory.js.map +0 -1
  294. package/dist/providers/compat/compatibility-interface.d.ts +0 -35
  295. package/dist/providers/compat/compatibility-interface.js +0 -2
  296. package/dist/providers/compat/compatibility-interface.js.map +0 -1
  297. package/dist/providers/compat/compatibility-manager.d.ts +0 -85
  298. package/dist/providers/compat/compatibility-manager.js +0 -368
  299. package/dist/providers/compat/compatibility-manager.js.map +0 -1
  300. package/dist/providers/compat/config/config-compatibility.d.ts +0 -28
  301. package/dist/providers/compat/config/config-compatibility.js +0 -95
  302. package/dist/providers/compat/config/config-compatibility.js.map +0 -1
  303. package/dist/providers/compat/field-mapping.d.ts +0 -102
  304. package/dist/providers/compat/field-mapping.js +0 -447
  305. package/dist/providers/compat/field-mapping.js.map +0 -1
  306. package/dist/providers/compat/filters/blacklist-sanitizer.d.ts +0 -45
  307. package/dist/providers/compat/filters/blacklist-sanitizer.js +0 -133
  308. package/dist/providers/compat/filters/blacklist-sanitizer.js.map +0 -1
  309. package/dist/providers/compat/filters/response-blacklist-sanitizer.d.ts +0 -28
  310. package/dist/providers/compat/filters/response-blacklist-sanitizer.js +0 -138
  311. package/dist/providers/compat/filters/response-blacklist-sanitizer.js.map +0 -1
  312. package/dist/providers/compat/filters/universal-shape-filter.js.map +0 -1
  313. package/dist/providers/compat/glm/config/blacklist-rules.json +0 -22
  314. package/dist/providers/compat/glm/config/field-mappings.json +0 -92
  315. package/dist/providers/compat/glm/config/response-blacklist.json +0 -7
  316. package/dist/providers/compat/glm/config/shape-filters.json +0 -37
  317. package/dist/providers/compat/glm/field-mapping/field-mapping-processor.d.ts +0 -28
  318. package/dist/providers/compat/glm/field-mapping/field-mapping-processor.js +0 -306
  319. package/dist/providers/compat/glm/field-mapping/field-mapping-processor.js.map +0 -1
  320. package/dist/providers/compat/glm/functions/glm-processor.d.ts +0 -50
  321. package/dist/providers/compat/glm/functions/glm-processor.js +0 -134
  322. package/dist/providers/compat/glm/functions/glm-processor.js.map +0 -1
  323. package/dist/providers/compat/glm/glm-compatibility.d.ts +0 -34
  324. package/dist/providers/compat/glm/glm-compatibility.js +0 -117
  325. package/dist/providers/compat/glm/glm-compatibility.js.map +0 -1
  326. package/dist/providers/compat/glm/hooks/base-hook.d.ts +0 -21
  327. package/dist/providers/compat/glm/hooks/base-hook.js +0 -53
  328. package/dist/providers/compat/glm/hooks/base-hook.js.map +0 -1
  329. package/dist/providers/compat/glm/hooks/glm-request-validation-hook.d.ts +0 -24
  330. package/dist/providers/compat/glm/hooks/glm-request-validation-hook.js +0 -268
  331. package/dist/providers/compat/glm/hooks/glm-request-validation-hook.js.map +0 -1
  332. package/dist/providers/compat/glm/hooks/glm-response-normalization-hook.d.ts +0 -21
  333. package/dist/providers/compat/glm/hooks/glm-response-normalization-hook.js +0 -171
  334. package/dist/providers/compat/glm/hooks/glm-response-normalization-hook.js.map +0 -1
  335. package/dist/providers/compat/glm/hooks/glm-response-validation-hook.d.ts +0 -25
  336. package/dist/providers/compat/glm/hooks/glm-response-validation-hook.js +0 -236
  337. package/dist/providers/compat/glm/hooks/glm-response-validation-hook.js.map +0 -1
  338. package/dist/providers/compat/glm/hooks/glm-tool-cleaning-hook.d.ts +0 -26
  339. package/dist/providers/compat/glm/hooks/glm-tool-cleaning-hook.js +0 -186
  340. package/dist/providers/compat/glm/hooks/glm-tool-cleaning-hook.js.map +0 -1
  341. package/dist/providers/compat/glm/index.d.ts +0 -24
  342. package/dist/providers/compat/glm/index.js +0 -29
  343. package/dist/providers/compat/glm/index.js.map +0 -1
  344. package/dist/providers/compat/glm/utils/tool-schema-helpers.d.ts +0 -3
  345. package/dist/providers/compat/glm/utils/tool-schema-helpers.js.map +0 -1
  346. package/dist/providers/compat/iflow/config/field-mappings.json +0 -92
  347. package/dist/providers/compat/iflow/config/shape-filters.json +0 -37
  348. package/dist/providers/compat/iflow/field-mapping/iflow-field-mapping-processor.d.ts +0 -34
  349. package/dist/providers/compat/iflow/field-mapping/iflow-field-mapping-processor.js +0 -386
  350. package/dist/providers/compat/iflow/field-mapping/iflow-field-mapping-processor.js.map +0 -1
  351. package/dist/providers/compat/iflow/functions/iflow-processor.d.ts +0 -53
  352. package/dist/providers/compat/iflow/functions/iflow-processor.js +0 -215
  353. package/dist/providers/compat/iflow/functions/iflow-processor.js.map +0 -1
  354. package/dist/providers/compat/iflow/hooks/base-hook.d.ts +0 -23
  355. package/dist/providers/compat/iflow/hooks/base-hook.js +0 -59
  356. package/dist/providers/compat/iflow/hooks/base-hook.js.map +0 -1
  357. package/dist/providers/compat/iflow/hooks/iflow-request-validation-hook.d.ts +0 -23
  358. package/dist/providers/compat/iflow/hooks/iflow-request-validation-hook.js +0 -279
  359. package/dist/providers/compat/iflow/hooks/iflow-request-validation-hook.js.map +0 -1
  360. package/dist/providers/compat/iflow/hooks/iflow-response-normalization-hook.d.ts +0 -20
  361. package/dist/providers/compat/iflow/hooks/iflow-response-normalization-hook.js +0 -180
  362. package/dist/providers/compat/iflow/hooks/iflow-response-normalization-hook.js.map +0 -1
  363. package/dist/providers/compat/iflow/hooks/iflow-response-validation-hook.d.ts +0 -23
  364. package/dist/providers/compat/iflow/hooks/iflow-response-validation-hook.js +0 -232
  365. package/dist/providers/compat/iflow/hooks/iflow-response-validation-hook.js.map +0 -1
  366. package/dist/providers/compat/iflow/hooks/iflow-tool-cleaning-hook.d.ts +0 -25
  367. package/dist/providers/compat/iflow/hooks/iflow-tool-cleaning-hook.js +0 -216
  368. package/dist/providers/compat/iflow/hooks/iflow-tool-cleaning-hook.js.map +0 -1
  369. package/dist/providers/compat/iflow/iflow-compatibility.d.ts +0 -24
  370. package/dist/providers/compat/iflow/iflow-compatibility.js +0 -94
  371. package/dist/providers/compat/iflow/iflow-compatibility.js.map +0 -1
  372. package/dist/providers/compat/index.d.ts +0 -59
  373. package/dist/providers/compat/index.js +0 -83
  374. package/dist/providers/compat/index.js.map +0 -1
  375. package/dist/providers/compat/lmstudio-compatibility.d.ts +0 -44
  376. package/dist/providers/compat/lmstudio-compatibility.js +0 -193
  377. package/dist/providers/compat/lmstudio-compatibility.js.map +0 -1
  378. package/dist/providers/compat/passthrough-compatibility.d.ts +0 -29
  379. package/dist/providers/compat/passthrough-compatibility.js +0 -83
  380. package/dist/providers/compat/passthrough-compatibility.js.map +0 -1
  381. package/dist/providers/compat/profiles/chat/glm/index.d.ts +0 -6
  382. package/dist/providers/compat/profiles/chat/glm/index.js +0 -6
  383. package/dist/providers/compat/profiles/chat/glm/index.js.map +0 -1
  384. package/dist/providers/compat/profiles/chat/iflow/index.d.ts +0 -6
  385. package/dist/providers/compat/profiles/chat/iflow/index.js +0 -6
  386. package/dist/providers/compat/profiles/chat/iflow/index.js.map +0 -1
  387. package/dist/providers/compat/profiles/chat/lmstudio/index.d.ts +0 -6
  388. package/dist/providers/compat/profiles/chat/lmstudio/index.js +0 -6
  389. package/dist/providers/compat/profiles/chat/lmstudio/index.js.map +0 -1
  390. package/dist/providers/compat/profiles/chat/qwen/index.d.ts +0 -6
  391. package/dist/providers/compat/profiles/chat/qwen/index.js +0 -6
  392. package/dist/providers/compat/profiles/chat/qwen/index.js.map +0 -1
  393. package/dist/providers/compat/profiles/compat/passthrough/index.d.ts +0 -6
  394. package/dist/providers/compat/profiles/compat/passthrough/index.js +0 -6
  395. package/dist/providers/compat/profiles/compat/passthrough/index.js.map +0 -1
  396. package/dist/providers/compat/profiles/responses/c4m/index.d.ts +0 -6
  397. package/dist/providers/compat/profiles/responses/c4m/index.js +0 -6
  398. package/dist/providers/compat/profiles/responses/c4m/index.js.map +0 -1
  399. package/dist/providers/compat/profiles/responses/default/index.d.ts +0 -6
  400. package/dist/providers/compat/profiles/responses/default/index.js +0 -6
  401. package/dist/providers/compat/profiles/responses/default/index.js.map +0 -1
  402. package/dist/providers/compat/profiles/responses/fai/index.d.ts +0 -6
  403. package/dist/providers/compat/profiles/responses/fai/index.js +0 -6
  404. package/dist/providers/compat/profiles/responses/fai/index.js.map +0 -1
  405. package/dist/providers/compat/profiles/responses/fc/index.d.ts +0 -6
  406. package/dist/providers/compat/profiles/responses/fc/index.js +0 -6
  407. package/dist/providers/compat/profiles/responses/fc/index.js.map +0 -1
  408. package/dist/providers/compat/qwen/index.d.ts +0 -4
  409. package/dist/providers/compat/qwen/index.js +0 -6
  410. package/dist/providers/compat/qwen/index.js.map +0 -1
  411. package/dist/providers/compat/qwen-compatibility.d.ts +0 -52
  412. package/dist/providers/compat/qwen-compatibility.js +0 -330
  413. package/dist/providers/compat/qwen-compatibility.js.map +0 -1
  414. package/dist/providers/compat/register-compat-module.d.ts +0 -8
  415. package/dist/providers/compat/register-compat-module.js +0 -53
  416. package/dist/providers/compat/register-compat-module.js.map +0 -1
  417. package/dist/providers/compat/responses/c4m-responses-compatibility.d.ts +0 -27
  418. package/dist/providers/compat/responses/c4m-responses-compatibility.js +0 -197
  419. package/dist/providers/compat/responses/c4m-responses-compatibility.js.map +0 -1
  420. package/dist/providers/compat/standard-compatibility-utils.d.ts +0 -1
  421. package/dist/providers/compat/standard-compatibility-utils.js +0 -77
  422. package/dist/providers/compat/standard-compatibility-utils.js.map +0 -1
  423. package/dist/providers/compat/standard-compatibility.d.ts +0 -31
  424. package/dist/providers/compat/standard-compatibility.js +0 -118
  425. package/dist/providers/compat/standard-compatibility.js.map +0 -1
  426. package/dist/providers/compat/utils/snapshot-writer.js +0 -62
  427. package/dist/providers/compat/utils/snapshot-writer.js.map +0 -1
  428. package/dist/tools/replay-request.d.ts +0 -0
  429. package/dist/tools/replay-request.js +0 -2
  430. package/dist/tools/replay-request.js.map +0 -1
  431. package/scripts/check-glm-compat.mjs +0 -47
@@ -36,6 +36,7 @@ export class HubRequestExecutor {
36
36
  }
37
37
  async execute(input) {
38
38
  this.deps.stats.recordRequestStart(input.requestId);
39
+ const requestStartedAt = Date.now();
39
40
  let statsRecorded = false;
40
41
  const finalizeStats = (options) => {
41
42
  if (statsRecorded) {
@@ -46,12 +47,13 @@ export class HubRequestExecutor {
46
47
  };
47
48
  try {
48
49
  const hubPipeline = this.ensureHubPipeline();
49
- const metadata = this.buildRequestMetadata(input);
50
+ const initialMetadata = this.buildRequestMetadata(input);
51
+ const inboundClientHeaders = this.cloneClientHeaders(initialMetadata?.clientHeaders);
50
52
  const providerRequestId = input.requestId;
51
- const clientRequestId = this.resolveClientRequestId(metadata, providerRequestId);
53
+ const clientRequestId = this.resolveClientRequestId(initialMetadata, providerRequestId);
52
54
  this.logStage('request.received', providerRequestId, {
53
55
  endpoint: input.entryEndpoint,
54
- stream: metadata.stream === true
56
+ stream: initialMetadata.stream === true
55
57
  });
56
58
  try {
57
59
  const headerUa = (typeof input.headers?.['user-agent'] === 'string' && input.headers['user-agent']) ||
@@ -64,7 +66,7 @@ export class HubRequestExecutor {
64
66
  headers: asRecord(input.headers),
65
67
  body: input.body,
66
68
  metadata: {
67
- ...metadata,
69
+ ...initialMetadata,
68
70
  userAgent: headerUa,
69
71
  clientOriginator: headerOriginator
70
72
  }
@@ -74,158 +76,187 @@ export class HubRequestExecutor {
74
76
  /* snapshot failure should not block request path */
75
77
  }
76
78
  const pipelineLabel = 'hub';
77
- this.logStage(`${pipelineLabel}.start`, providerRequestId, {
78
- endpoint: input.entryEndpoint,
79
- stream: metadata.stream
80
- });
81
- const originalRequestSnapshot = this.cloneRequestPayload(input.body);
82
- const pipelineResult = await this.runHubPipeline(hubPipeline, input, metadata);
83
- const pipelineMetadata = pipelineResult.metadata ?? {};
84
- const mergedMetadata = { ...metadata, ...pipelineMetadata };
85
- this.logStage(`${pipelineLabel}.completed`, providerRequestId, {
86
- route: pipelineResult.routingDecision?.routeName,
87
- target: pipelineResult.target?.providerKey
88
- });
89
- const providerPayload = pipelineResult.providerPayload;
90
- const target = pipelineResult.target;
91
- if (!providerPayload || !target?.providerKey) {
92
- throw Object.assign(new Error('Virtual router did not produce a provider target'), {
93
- code: 'ERR_NO_PROVIDER_TARGET',
94
- requestId: input.requestId
79
+ let iterationMetadata = initialMetadata;
80
+ let aggregatedUsage;
81
+ let attempt = 0;
82
+ let followupTriggered = false;
83
+ while (true) {
84
+ this.logStage(`${pipelineLabel}.start`, providerRequestId, {
85
+ endpoint: input.entryEndpoint,
86
+ stream: iterationMetadata.stream,
87
+ attempt
95
88
  });
96
- }
97
- const runtimeKey = target.runtimeKey || this.deps.runtimeManager.resolveRuntimeKey(target.providerKey);
98
- if (!runtimeKey) {
99
- throw Object.assign(new Error(`Runtime for provider ${target.providerKey} not initialized`), {
100
- code: 'ERR_RUNTIME_NOT_FOUND',
101
- requestId: input.requestId
102
- });
103
- }
104
- const handle = this.deps.runtimeManager.getHandleByRuntimeKey(runtimeKey);
105
- if (!handle) {
106
- throw Object.assign(new Error(`Provider runtime ${runtimeKey} not found`), {
107
- code: 'ERR_PROVIDER_NOT_FOUND',
108
- requestId: input.requestId
89
+ // 获取原始请求快照,用于后续响应转换(不包含任何 server-side 工具语义)。
90
+ const originalRequestSnapshot = this.cloneRequestPayload(input.body);
91
+ const pipelineResult = await this.runHubPipeline(hubPipeline, input, iterationMetadata);
92
+ const pipelineMetadata = pipelineResult.metadata ?? {};
93
+ const mergedMetadata = { ...iterationMetadata, ...pipelineMetadata };
94
+ const mergedClientHeaders = this.cloneClientHeaders(mergedMetadata?.clientHeaders) || inboundClientHeaders;
95
+ if (mergedClientHeaders) {
96
+ mergedMetadata.clientHeaders = mergedClientHeaders;
97
+ }
98
+ mergedMetadata.clientRequestId = clientRequestId;
99
+ this.logStage(`${pipelineLabel}.completed`, providerRequestId, {
100
+ route: pipelineResult.routingDecision?.routeName,
101
+ target: pipelineResult.target?.providerKey,
102
+ attempt
109
103
  });
110
- }
111
- const providerProtocol = target.outboundProfile || handle.providerProtocol;
112
- const metadataModel = mergedMetadata?.target && typeof mergedMetadata.target === 'object'
113
- ? mergedMetadata.target.clientModelId
114
- : undefined;
115
- const rawModel = this.extractProviderModel(providerPayload) ||
116
- (typeof metadataModel === 'string' ? metadataModel : undefined);
117
- const providerAlias = typeof target.providerKey === 'string' && target.providerKey.includes('.')
118
- ? target.providerKey.split('.').slice(0, 2).join('.')
119
- : target.providerKey;
120
- const providerIdToken = providerAlias || handle.providerId || runtimeKey;
121
- if (!providerIdToken) {
122
- throw Object.assign(new Error('Provider identifier missing for request'), {
123
- code: 'ERR_PROVIDER_ID_MISSING',
124
- requestId: providerRequestId
104
+ const providerPayload = pipelineResult.providerPayload;
105
+ const target = pipelineResult.target;
106
+ if (!providerPayload || !target?.providerKey) {
107
+ throw Object.assign(new Error('Virtual router did not produce a provider target'), {
108
+ code: 'ERR_NO_PROVIDER_TARGET',
109
+ requestId: input.requestId
110
+ });
111
+ }
112
+ const runtimeKey = target.runtimeKey || this.deps.runtimeManager.resolveRuntimeKey(target.providerKey);
113
+ if (!runtimeKey) {
114
+ throw Object.assign(new Error(`Runtime for provider ${target.providerKey} not initialized`), {
115
+ code: 'ERR_RUNTIME_NOT_FOUND',
116
+ requestId: input.requestId
117
+ });
118
+ }
119
+ const handle = this.deps.runtimeManager.getHandleByRuntimeKey(runtimeKey);
120
+ if (!handle) {
121
+ throw Object.assign(new Error(`Provider runtime ${runtimeKey} not found`), {
122
+ code: 'ERR_PROVIDER_NOT_FOUND',
123
+ requestId: input.requestId
124
+ });
125
+ }
126
+ const providerProtocol = target.outboundProfile || handle.providerProtocol;
127
+ const metadataModel = mergedMetadata?.target && typeof mergedMetadata.target === 'object'
128
+ ? mergedMetadata.target.clientModelId
129
+ : undefined;
130
+ const rawModel = this.extractProviderModel(providerPayload) ||
131
+ (typeof metadataModel === 'string' ? metadataModel : undefined);
132
+ const providerAlias = typeof target.providerKey === 'string' && target.providerKey.includes('.')
133
+ ? target.providerKey.split('.').slice(0, 2).join('.')
134
+ : target.providerKey;
135
+ const providerIdToken = providerAlias || handle.providerId || runtimeKey;
136
+ if (!providerIdToken) {
137
+ throw Object.assign(new Error('Provider identifier missing for request'), {
138
+ code: 'ERR_PROVIDER_ID_MISSING',
139
+ requestId: providerRequestId
140
+ });
141
+ }
142
+ const enhancedRequestId = enhanceProviderRequestId(providerRequestId, {
143
+ entryEndpoint: input.entryEndpoint,
144
+ providerId: providerIdToken,
145
+ model: rawModel
125
146
  });
126
- }
127
- const enhancedRequestId = enhanceProviderRequestId(providerRequestId, {
128
- entryEndpoint: input.entryEndpoint,
129
- providerId: providerIdToken,
130
- model: rawModel
131
- });
132
- if (enhancedRequestId !== input.requestId) {
133
- input.requestId = enhancedRequestId;
134
- }
135
- mergedMetadata.clientRequestId = clientRequestId;
136
- const providerModel = rawModel;
137
- const providerLabel = this.buildProviderLabel(target.providerKey, providerModel);
138
- this.deps.stats.bindProvider(input.requestId, {
139
- providerKey: target.providerKey,
140
- providerType: handle.providerType,
141
- model: providerModel
142
- });
143
- this.logStage('provider.prepare', input.requestId, {
144
- providerKey: target.providerKey,
145
- runtimeKey,
146
- protocol: providerProtocol,
147
- providerType: handle.providerType,
148
- providerFamily: handle.providerFamily,
149
- model: providerModel,
150
- providerLabel
151
- });
152
- attachProviderRuntimeMetadata(providerPayload, {
153
- requestId: input.requestId,
154
- providerId: handle.providerId,
155
- providerKey: target.providerKey,
156
- providerType: handle.providerType,
157
- providerFamily: handle.providerFamily,
158
- providerProtocol,
159
- pipelineId: target.providerKey,
160
- routeName: pipelineResult.routingDecision?.routeName,
161
- runtimeKey,
162
- target,
163
- metadata: mergedMetadata,
164
- compatibilityProfile: target.compatibilityProfile
165
- });
166
- this.logStage('provider.send.start', input.requestId, {
167
- providerKey: target.providerKey,
168
- runtimeKey,
169
- protocol: providerProtocol,
170
- providerType: handle.providerType,
171
- providerFamily: handle.providerFamily,
172
- model: providerModel,
173
- providerLabel
174
- });
175
- try {
176
- const providerResponse = await handle.instance.processIncoming(providerPayload);
177
- const responseStatus = this.extractResponseStatus(providerResponse);
178
- this.logStage('provider.send.completed', input.requestId, {
147
+ if (enhancedRequestId !== input.requestId) {
148
+ input.requestId = enhancedRequestId;
149
+ }
150
+ mergedMetadata.clientRequestId = clientRequestId;
151
+ const providerModel = rawModel;
152
+ const providerLabel = this.buildProviderLabel(target.providerKey, providerModel);
153
+ if (inboundClientHeaders) {
154
+ this.ensureClientHeadersOnPayload(providerPayload, inboundClientHeaders);
155
+ }
156
+ this.deps.stats.bindProvider(input.requestId, {
179
157
  providerKey: target.providerKey,
180
- status: responseStatus,
181
158
  providerType: handle.providerType,
182
- providerFamily: handle.providerFamily,
183
- model: providerModel,
184
- providerLabel
159
+ model: providerModel
185
160
  });
186
- const normalized = this.normalizeProviderResponse(providerResponse);
187
- const converted = await this.convertProviderResponseIfNeeded({
188
- entryEndpoint: input.entryEndpoint,
189
- providerType: handle.providerType,
190
- requestId: input.requestId,
191
- wantsStream: Boolean(input.metadata?.inboundStream ?? input.metadata?.stream),
192
- originalRequest: originalRequestSnapshot,
193
- processMode: pipelineResult.processMode,
194
- response: normalized,
195
- pipelineMetadata: mergedMetadata
196
- });
197
- const usage = this.extractUsageFromResult(converted, mergedMetadata);
198
- finalizeStats({ usage, error: false });
199
- return converted;
200
- }
201
- catch (error) {
202
- this.logStage('provider.send.error', input.requestId, {
161
+ this.logStage('provider.prepare', input.requestId, {
203
162
  providerKey: target.providerKey,
204
- message: error instanceof Error ? error.message : String(error ?? 'Unknown error'),
163
+ runtimeKey,
164
+ protocol: providerProtocol,
205
165
  providerType: handle.providerType,
206
166
  providerFamily: handle.providerFamily,
207
167
  model: providerModel,
208
- providerLabel
168
+ providerLabel,
169
+ attempt
209
170
  });
210
- const runtimeMetadata = {
171
+ attachProviderRuntimeMetadata(providerPayload, {
211
172
  requestId: input.requestId,
212
- providerKey: target.providerKey,
213
173
  providerId: handle.providerId,
174
+ providerKey: target.providerKey,
214
175
  providerType: handle.providerType,
176
+ providerFamily: handle.providerFamily,
215
177
  providerProtocol,
216
- routeName: pipelineResult.routingDecision?.routeName,
217
178
  pipelineId: target.providerKey,
179
+ routeName: pipelineResult.routingDecision?.routeName,
218
180
  runtimeKey,
219
- target
220
- };
221
- runtimeMetadata.providerFamily = handle.providerFamily;
222
- emitProviderError({
223
- error,
224
- stage: 'provider.send',
225
- runtime: runtimeMetadata,
226
- dependencies: this.deps.getModuleDependencies()
181
+ target,
182
+ metadata: mergedMetadata,
183
+ compatibilityProfile: target.compatibilityProfile
227
184
  });
228
- throw error;
185
+ this.logStage('provider.send.start', input.requestId, {
186
+ providerKey: target.providerKey,
187
+ runtimeKey,
188
+ protocol: providerProtocol,
189
+ providerType: handle.providerType,
190
+ providerFamily: handle.providerFamily,
191
+ model: providerModel,
192
+ providerLabel,
193
+ attempt
194
+ });
195
+ try {
196
+ const providerResponse = await handle.instance.processIncoming(providerPayload);
197
+ const responseStatus = this.extractResponseStatus(providerResponse);
198
+ this.logStage('provider.send.completed', input.requestId, {
199
+ providerKey: target.providerKey,
200
+ status: responseStatus,
201
+ providerType: handle.providerType,
202
+ providerFamily: handle.providerFamily,
203
+ model: providerModel,
204
+ providerLabel,
205
+ attempt
206
+ });
207
+ const wantsStreamBase = Boolean(input.metadata?.inboundStream ?? input.metadata?.stream);
208
+ const normalized = this.normalizeProviderResponse(providerResponse);
209
+ const converted = await this.convertProviderResponseIfNeeded({
210
+ entryEndpoint: input.entryEndpoint,
211
+ providerType: handle.providerType,
212
+ requestId: input.requestId,
213
+ wantsStream: wantsStreamBase,
214
+ originalRequest: originalRequestSnapshot,
215
+ processMode: pipelineResult.processMode,
216
+ response: normalized,
217
+ pipelineMetadata: mergedMetadata
218
+ });
219
+ const usage = this.extractUsageFromResult(converted, mergedMetadata);
220
+ aggregatedUsage = this.mergeUsageMetrics(aggregatedUsage, usage);
221
+ finalizeStats({ usage: aggregatedUsage, error: false });
222
+ this.logUsageSummary(input.requestId, {
223
+ providerKey: target.providerKey,
224
+ model: providerModel,
225
+ usage: aggregatedUsage,
226
+ latencyMs: Date.now() - requestStartedAt
227
+ });
228
+ return converted;
229
+ }
230
+ catch (error) {
231
+ this.logStage('provider.send.error', input.requestId, {
232
+ providerKey: target.providerKey,
233
+ message: error instanceof Error ? error.message : String(error ?? 'Unknown error'),
234
+ providerType: handle.providerType,
235
+ providerFamily: handle.providerFamily,
236
+ model: providerModel,
237
+ providerLabel,
238
+ attempt
239
+ });
240
+ const runtimeMetadata = {
241
+ requestId: input.requestId,
242
+ providerKey: target.providerKey,
243
+ providerId: handle.providerId,
244
+ providerType: handle.providerType,
245
+ providerProtocol,
246
+ routeName: pipelineResult.routingDecision?.routeName,
247
+ pipelineId: target.providerKey,
248
+ runtimeKey,
249
+ target
250
+ };
251
+ runtimeMetadata.providerFamily = handle.providerFamily;
252
+ emitProviderError({
253
+ error,
254
+ stage: 'provider.send',
255
+ runtime: runtimeMetadata,
256
+ dependencies: this.deps.getModuleDependencies()
257
+ });
258
+ throw error;
259
+ }
229
260
  }
230
261
  }
231
262
  catch (error) {
@@ -265,6 +296,31 @@ export class HubRequestExecutor {
265
296
  metadata: result.metadata ?? {}
266
297
  };
267
298
  }
299
+ cloneClientHeaders(source) {
300
+ if (!source || typeof source !== 'object') {
301
+ return undefined;
302
+ }
303
+ const normalized = {};
304
+ for (const [key, value] of Object.entries(source)) {
305
+ if (typeof value === 'string' && value.trim()) {
306
+ normalized[key] = value;
307
+ }
308
+ }
309
+ return Object.keys(normalized).length ? normalized : undefined;
310
+ }
311
+ ensureClientHeadersOnPayload(payload, headers) {
312
+ if (!payload || typeof payload !== 'object') {
313
+ return;
314
+ }
315
+ const carrier = payload;
316
+ const existing = carrier.metadata && typeof carrier.metadata === 'object'
317
+ ? carrier.metadata
318
+ : {};
319
+ carrier.metadata = {
320
+ ...existing,
321
+ clientHeaders: existing.clientHeaders ?? headers
322
+ };
323
+ }
268
324
  resolveClientRequestId(metadata, fallback) {
269
325
  const clientRequestId = typeof metadata.clientRequestId === 'string' && metadata.clientRequestId.trim()
270
326
  ? metadata.clientRequestId.trim()
@@ -273,6 +329,15 @@ export class HubRequestExecutor {
273
329
  }
274
330
  buildRequestMetadata(input) {
275
331
  const userMeta = asRecord(input.metadata);
332
+ const headers = asRecord(input.headers);
333
+ const inboundUserAgent = this.extractHeaderValue(headers, 'user-agent');
334
+ const inboundOriginator = this.extractHeaderValue(headers, 'originator');
335
+ const resolvedUserAgent = typeof userMeta.userAgent === 'string' && userMeta.userAgent.trim()
336
+ ? userMeta.userAgent.trim()
337
+ : inboundUserAgent;
338
+ const resolvedOriginator = typeof userMeta.clientOriginator === 'string' && userMeta.clientOriginator.trim()
339
+ ? userMeta.clientOriginator.trim()
340
+ : inboundOriginator;
276
341
  const routeHint = this.extractRouteHint(input) ?? userMeta.routeHint;
277
342
  const processMode = userMeta.processMode || 'chat';
278
343
  return {
@@ -282,9 +347,30 @@ export class HubRequestExecutor {
282
347
  direction: 'request',
283
348
  stage: 'inbound',
284
349
  routeHint,
285
- stream: userMeta.stream === true
350
+ stream: userMeta.stream === true,
351
+ ...(resolvedUserAgent ? { userAgent: resolvedUserAgent } : {}),
352
+ ...(resolvedOriginator ? { clientOriginator: resolvedOriginator } : {})
286
353
  };
287
354
  }
355
+ extractHeaderValue(headers, name) {
356
+ if (!headers) {
357
+ return undefined;
358
+ }
359
+ const target = name.toLowerCase();
360
+ for (const [key, value] of Object.entries(headers)) {
361
+ if (key.toLowerCase() !== target) {
362
+ continue;
363
+ }
364
+ if (typeof value === 'string') {
365
+ return value.trim() || undefined;
366
+ }
367
+ if (Array.isArray(value) && value.length) {
368
+ return String(value[0]).trim() || undefined;
369
+ }
370
+ return undefined;
371
+ }
372
+ return undefined;
373
+ }
288
374
  extractRouteHint(input) {
289
375
  const header = input.headers?.['x-route-hint'];
290
376
  if (typeof header === 'string' && header.trim()) {
@@ -342,12 +428,17 @@ export class HubRequestExecutor {
342
428
  const metadataBag = asRecord(options.pipelineMetadata);
343
429
  const aliasMap = extractAnthropicToolAliasMap(metadataBag);
344
430
  const originalModelId = this.extractClientModelId(metadataBag, options.originalRequest);
345
- const adapterContext = {
346
- requestId: options.requestId,
347
- entryEndpoint: options.entryEndpoint || entry,
348
- providerProtocol,
349
- originalModelId
431
+ const baseContext = {
432
+ ...(metadataBag ?? {})
350
433
  };
434
+ if (typeof metadataBag?.routeName === 'string') {
435
+ baseContext.routeId = metadataBag.routeName;
436
+ }
437
+ baseContext.requestId = options.requestId;
438
+ baseContext.entryEndpoint = options.entryEndpoint || entry;
439
+ baseContext.providerProtocol = providerProtocol;
440
+ baseContext.originalModelId = originalModelId;
441
+ const adapterContext = baseContext;
351
442
  if (aliasMap) {
352
443
  adapterContext.anthropicToolNameMap = aliasMap;
353
444
  }
@@ -355,14 +446,85 @@ export class HubRequestExecutor {
355
446
  loadConvertProviderResponse(),
356
447
  loadSnapshotRecorderFactory()
357
448
  ]);
358
- const stageRecorder = createSnapshotRecorder(adapterContext, adapterContext.entryEndpoint);
449
+ const stageRecorder = createSnapshotRecorder(adapterContext, typeof adapterContext.entryEndpoint === 'string'
450
+ ? adapterContext.entryEndpoint
451
+ : options.entryEndpoint || entry);
452
+ const providerInvoker = async (invokeOptions) => {
453
+ // 将 server-side 工具的 routeHint 注入到内部 payload 的 metadata,
454
+ // 以便后续在标准 HubPipeline 中保持路由上下文一致(例如强制 web_search)。
455
+ if (invokeOptions.routeHint) {
456
+ const carrier = invokeOptions.payload;
457
+ const existingMeta = carrier.metadata && typeof carrier.metadata === 'object'
458
+ ? carrier.metadata
459
+ : {};
460
+ carrier.metadata = {
461
+ ...existingMeta,
462
+ routeHint: existingMeta.routeHint ?? invokeOptions.routeHint
463
+ };
464
+ }
465
+ // Delegate to existing runtimeManager / Provider V2 stack.
466
+ const runtimeKey = this.deps.runtimeManager.resolveRuntimeKey(invokeOptions.providerKey);
467
+ if (!runtimeKey) {
468
+ throw new Error(`Runtime for provider ${invokeOptions.providerKey} not initialized`);
469
+ }
470
+ const handle = this.deps.runtimeManager.getHandleByRuntimeKey(runtimeKey);
471
+ if (!handle) {
472
+ throw new Error(`Provider runtime ${runtimeKey} not found`);
473
+ }
474
+ const providerResponse = await handle.instance.processIncoming(invokeOptions.payload);
475
+ const normalized = this.normalizeProviderResponse(providerResponse);
476
+ const bodyPayload = normalized.body && typeof normalized.body === 'object'
477
+ ? normalized.body
478
+ : normalized;
479
+ return { providerResponse: bodyPayload };
480
+ };
481
+ const reenterPipeline = async (reenterOpts) => {
482
+ const nestedEntry = reenterOpts.entryEndpoint || options.entryEndpoint || entry;
483
+ const nestedExtra = asRecord(reenterOpts.metadata) ?? {};
484
+ const nestedEntryLower = nestedEntry.toLowerCase();
485
+ // 基于首次 HubPipeline metadata + 调用方注入的 metadata 构建新的请求 metadata。
486
+ // 不在 Host 层编码 servertool/web_search 等语义,由 llmswitch-core 负责。
487
+ const nestedMetadata = {
488
+ ...(metadataBag ?? {}),
489
+ ...nestedExtra,
490
+ entryEndpoint: nestedEntry,
491
+ direction: 'request',
492
+ stage: 'inbound'
493
+ };
494
+ // 针对 reenterPipeline 的入口端点,纠正 providerProtocol,避免沿用外层协议。
495
+ if (nestedEntryLower.includes('/v1/chat/completions')) {
496
+ nestedMetadata.providerProtocol = 'openai-chat';
497
+ }
498
+ else if (nestedEntryLower.includes('/v1/responses')) {
499
+ nestedMetadata.providerProtocol = 'openai-responses';
500
+ }
501
+ else if (nestedEntryLower.includes('/v1/messages')) {
502
+ nestedMetadata.providerProtocol = 'anthropic-messages';
503
+ }
504
+ const nestedInput = {
505
+ entryEndpoint: nestedEntry,
506
+ method: 'POST',
507
+ requestId: reenterOpts.requestId,
508
+ headers: {},
509
+ query: {},
510
+ body: reenterOpts.body,
511
+ metadata: nestedMetadata
512
+ };
513
+ const nestedResult = await this.execute(nestedInput);
514
+ const nestedBody = nestedResult.body && typeof nestedResult.body === 'object'
515
+ ? nestedResult.body
516
+ : undefined;
517
+ return { body: nestedBody };
518
+ };
359
519
  const converted = await convertProviderResponse({
360
520
  providerProtocol,
361
521
  providerResponse: body,
362
522
  context: adapterContext,
363
523
  entryEndpoint: options.entryEndpoint || entry,
364
524
  wantsStream: options.wantsStream,
365
- stageRecorder
525
+ providerInvoker,
526
+ stageRecorder,
527
+ reenterPipeline
366
528
  });
367
529
  if (converted.__sse_responses) {
368
530
  return {
@@ -376,6 +538,20 @@ export class HubRequestExecutor {
376
538
  };
377
539
  }
378
540
  catch (error) {
541
+ const err = error;
542
+ const message = err instanceof Error ? err.message : String(err ?? 'Unknown error');
543
+ const providerProtocol = mapProviderProtocol(options.providerType);
544
+ // 对于 Gemini 等基于 SSE 的 provider,如果 llmswitch-core 报告
545
+ // “Failed to convert SSE payload ...” 之类错误,说明上游流式响应已异常
546
+ // 终止(如 Cloud Code 终止/上下文超限),继续回退到原始 SSE 只会让
547
+ // 客户端挂起。因此在此类场景下直接抛出错误,让 HTTP 层返回明确的
548
+ // 5xx/4xx,而不是静默退回原始 payload。
549
+ const isSseConvertFailure = typeof message === 'string' &&
550
+ message.toLowerCase().includes('failed to convert sse payload');
551
+ if (providerProtocol === 'gemini-chat' && isSseConvertFailure) {
552
+ console.error('[RequestExecutor] Fatal SSE decode error for Gemini provider, bubbling as HTTP error', error);
553
+ throw error;
554
+ }
379
555
  console.error('[RequestExecutor] Failed to convert provider response via llmswitch-core', error);
380
556
  return options.response;
381
557
  }
@@ -439,6 +615,18 @@ export class HubRequestExecutor {
439
615
  logStage(stage, requestId, details) {
440
616
  this.deps.logStage(stage, requestId, details);
441
617
  }
618
+ logUsageSummary(requestId, info) {
619
+ if (process.env.ROUTECODEX_USAGE_LOG === '0') {
620
+ return;
621
+ }
622
+ const providerLabel = this.buildProviderLabel(info.providerKey, info.model) ?? '-';
623
+ const prompt = info.usage?.prompt_tokens;
624
+ const completion = info.usage?.completion_tokens;
625
+ const total = info.usage?.total_tokens ?? (prompt !== undefined && completion !== undefined ? prompt + completion : undefined);
626
+ const usageText = `prompt=${prompt ?? 'n/a'} completion=${completion ?? 'n/a'} total=${total ?? 'n/a'}`;
627
+ const latency = info.latencyMs.toFixed(1);
628
+ console.log(`[usage] request ${requestId} provider=${providerLabel} latency=${latency}ms (${usageText})`);
629
+ }
442
630
  extractUsageFromResult(result, metadata) {
443
631
  const candidates = [];
444
632
  if (metadata && typeof metadata === 'object') {
@@ -472,9 +660,22 @@ export class HubRequestExecutor {
472
660
  return undefined;
473
661
  }
474
662
  const record = value;
475
- const prompt = typeof record.prompt_tokens === 'number' ? record.prompt_tokens : undefined;
476
- const completion = typeof record.completion_tokens === 'number' ? record.completion_tokens : undefined;
477
- const total = typeof record.total_tokens === 'number' ? record.total_tokens : undefined;
663
+ const prompt = typeof record.prompt_tokens === 'number'
664
+ ? record.prompt_tokens
665
+ : typeof record.input_tokens === 'number'
666
+ ? record.input_tokens
667
+ : undefined;
668
+ const completion = typeof record.completion_tokens === 'number'
669
+ ? record.completion_tokens
670
+ : typeof record.output_tokens === 'number'
671
+ ? record.output_tokens
672
+ : undefined;
673
+ let total = typeof record.total_tokens === 'number'
674
+ ? record.total_tokens
675
+ : undefined;
676
+ if (total === undefined && prompt !== undefined && completion !== undefined) {
677
+ total = prompt + completion;
678
+ }
478
679
  if (prompt === undefined && completion === undefined && total === undefined) {
479
680
  return undefined;
480
681
  }
@@ -484,6 +685,294 @@ export class HubRequestExecutor {
484
685
  total_tokens: total
485
686
  };
486
687
  }
688
+ mergeUsageMetrics(base, delta) {
689
+ if (!delta) {
690
+ return base;
691
+ }
692
+ if (!base) {
693
+ return { ...delta };
694
+ }
695
+ const merged = {
696
+ prompt_tokens: (base.prompt_tokens ?? 0) + (delta.prompt_tokens ?? 0),
697
+ completion_tokens: (base.completion_tokens ?? 0) + (delta.completion_tokens ?? 0)
698
+ };
699
+ const total = (base.total_tokens ?? 0) + (delta.total_tokens ?? 0);
700
+ merged.total_tokens = total || undefined;
701
+ return merged;
702
+ }
703
+ buildVisionFollowupPayload(options) {
704
+ const { originalPayload, visionResponse } = options;
705
+ if (!originalPayload || typeof originalPayload !== 'object') {
706
+ return null;
707
+ }
708
+ const clone = this.cloneRequestPayload(originalPayload) ?? { ...originalPayload };
709
+ if (!clone) {
710
+ return null;
711
+ }
712
+ const visionText = this.extractVisionDescription(visionResponse?.body);
713
+ if (!visionText) {
714
+ return null;
715
+ }
716
+ if (this.rewriteResponsesInput(clone, visionText)) {
717
+ return clone;
718
+ }
719
+ if (this.rewriteChatMessages(clone, visionText)) {
720
+ return clone;
721
+ }
722
+ return null;
723
+ }
724
+ rewriteResponsesInput(payload, visionText) {
725
+ const inputList = payload.input;
726
+ if (!Array.isArray(inputList)) {
727
+ return false;
728
+ }
729
+ for (let i = inputList.length - 1; i >= 0; i -= 1) {
730
+ const item = inputList[i];
731
+ if (!item || typeof item !== 'object') {
732
+ continue;
733
+ }
734
+ const role = typeof item.role === 'string'
735
+ ? item.role
736
+ : '';
737
+ if (role !== 'user') {
738
+ continue;
739
+ }
740
+ const contentBlocks = Array.isArray(item.content)
741
+ ? [...item.content]
742
+ : [];
743
+ const originalText = this.extractTextFromContentBlocks(contentBlocks, ['input_text', 'text']);
744
+ const textType = this.detectContentTextType(contentBlocks, 'input_text');
745
+ const composed = this.composeVisionUserText(visionText, originalText);
746
+ item.content = [
747
+ {
748
+ type: textType,
749
+ text: composed
750
+ }
751
+ ];
752
+ inputList[i] = item;
753
+ return true;
754
+ }
755
+ return false;
756
+ }
757
+ rewriteChatMessages(payload, visionText) {
758
+ const messages = payload.messages;
759
+ if (!Array.isArray(messages)) {
760
+ return false;
761
+ }
762
+ for (let i = messages.length - 1; i >= 0; i -= 1) {
763
+ const message = messages[i];
764
+ if (!message || typeof message !== 'object') {
765
+ continue;
766
+ }
767
+ const role = typeof message.role === 'string'
768
+ ? message.role
769
+ : '';
770
+ if (role !== 'user') {
771
+ continue;
772
+ }
773
+ const contentBlocks = Array.isArray(message.content)
774
+ ? [...message.content]
775
+ : [];
776
+ const originalText = this.extractTextFromContentBlocks(contentBlocks, ['text']);
777
+ const textType = this.detectContentTextType(contentBlocks, 'text');
778
+ const composed = this.composeVisionUserText(visionText, originalText);
779
+ message.content = [
780
+ {
781
+ type: textType,
782
+ text: composed
783
+ }
784
+ ];
785
+ messages[i] = message;
786
+ return true;
787
+ }
788
+ return false;
789
+ }
790
+ extractTextFromContentBlocks(content, allowedTypes) {
791
+ if (typeof content === 'string') {
792
+ return content;
793
+ }
794
+ if (!Array.isArray(content)) {
795
+ return '';
796
+ }
797
+ const collected = [];
798
+ for (const block of content) {
799
+ if (!block || typeof block !== 'object') {
800
+ continue;
801
+ }
802
+ const typeValue = typeof block.type === 'string'
803
+ ? block.type
804
+ : '';
805
+ if (allowedTypes.length && !allowedTypes.includes(typeValue)) {
806
+ continue;
807
+ }
808
+ const textValue = block.text;
809
+ if (typeof textValue === 'string' && textValue.trim()) {
810
+ collected.push(textValue.trim());
811
+ }
812
+ }
813
+ return collected.join('\n');
814
+ }
815
+ detectContentTextType(content, fallback) {
816
+ if (Array.isArray(content)) {
817
+ for (const block of content) {
818
+ if (!block || typeof block !== 'object') {
819
+ continue;
820
+ }
821
+ const typeValue = block.type;
822
+ if (typeof typeValue === 'string' && (typeValue === 'text' || typeValue === 'input_text')) {
823
+ return typeValue;
824
+ }
825
+ }
826
+ }
827
+ return fallback;
828
+ }
829
+ composeVisionUserText(visionText, originalText) {
830
+ const sections = [];
831
+ const cleanedVision = (visionText || '').trim();
832
+ if (cleanedVision) {
833
+ sections.push(`【图片分析】\n${cleanedVision}`);
834
+ }
835
+ const cleanedOriginal = (originalText || '').trim();
836
+ if (cleanedOriginal) {
837
+ sections.push(`【用户原始请求】\n${cleanedOriginal}`);
838
+ }
839
+ return sections.join('\n\n');
840
+ }
841
+ extractVisionDescription(body) {
842
+ if (!body) {
843
+ return null;
844
+ }
845
+ if (typeof body === 'string') {
846
+ const trimmed = body.trim();
847
+ return trimmed.length ? trimmed : null;
848
+ }
849
+ if (typeof body !== 'object') {
850
+ return null;
851
+ }
852
+ const record = body;
853
+ const direct = this.extractTextCandidate(record);
854
+ if (direct) {
855
+ return direct;
856
+ }
857
+ if (record.response && typeof record.response === 'object') {
858
+ const responseNode = record.response;
859
+ const nested = this.extractTextCandidate(responseNode);
860
+ if (nested) {
861
+ return nested;
862
+ }
863
+ const output = responseNode.output;
864
+ if (Array.isArray(output)) {
865
+ for (const entry of output) {
866
+ if (entry && typeof entry === 'object') {
867
+ const nestedText = this.extractTextCandidate(entry);
868
+ if (nestedText) {
869
+ return nestedText;
870
+ }
871
+ }
872
+ }
873
+ }
874
+ }
875
+ if (Array.isArray(record.output)) {
876
+ for (const entry of record.output) {
877
+ if (entry && typeof entry === 'object') {
878
+ const nested = this.extractTextCandidate(entry);
879
+ if (nested) {
880
+ return nested;
881
+ }
882
+ }
883
+ }
884
+ }
885
+ const choices = record.choices;
886
+ if (Array.isArray(choices)) {
887
+ for (const choice of choices) {
888
+ if (!choice || typeof choice !== 'object') {
889
+ continue;
890
+ }
891
+ const message = choice.message;
892
+ if (message && typeof message === 'object') {
893
+ const msg = message;
894
+ const content = msg.content;
895
+ if (typeof content === 'string' && content.trim()) {
896
+ return content.trim();
897
+ }
898
+ if (Array.isArray(content)) {
899
+ for (const part of content) {
900
+ if (part && typeof part === 'object' && typeof part.text === 'string') {
901
+ const textValue = part.text.trim();
902
+ if (textValue) {
903
+ return textValue;
904
+ }
905
+ }
906
+ }
907
+ }
908
+ }
909
+ }
910
+ }
911
+ return null;
912
+ }
913
+ extractTextCandidate(record) {
914
+ const candidates = [
915
+ { key: 'output_text', allowJson: true },
916
+ { key: 'text' },
917
+ { key: 'content' }
918
+ ];
919
+ for (const candidate of candidates) {
920
+ if (!(candidate.key in record)) {
921
+ continue;
922
+ }
923
+ const text = this.normalizeTextCandidateValue(record[candidate.key], candidate.allowJson === true);
924
+ if (text) {
925
+ return text;
926
+ }
927
+ }
928
+ return null;
929
+ }
930
+ normalizeTextCandidateValue(value, allowJsonStringify = false) {
931
+ if (!value) {
932
+ return null;
933
+ }
934
+ if (typeof value === 'string') {
935
+ const trimmed = value.trim();
936
+ return trimmed.length ? trimmed : null;
937
+ }
938
+ if (Array.isArray(value)) {
939
+ const collected = [];
940
+ for (const entry of value) {
941
+ const nested = this.normalizeTextCandidateValue(entry, allowJsonStringify);
942
+ if (nested) {
943
+ collected.push(nested);
944
+ }
945
+ }
946
+ return collected.length ? collected.join('\n') : null;
947
+ }
948
+ if (typeof value === 'object') {
949
+ const bag = value;
950
+ const textField = bag.text;
951
+ if (typeof textField === 'string' && textField.trim()) {
952
+ return textField.trim();
953
+ }
954
+ const summaryField = bag.summary;
955
+ if (typeof summaryField === 'string' && summaryField.trim()) {
956
+ return summaryField.trim();
957
+ }
958
+ if ('content' in bag) {
959
+ const nested = this.normalizeTextCandidateValue(bag.content, allowJsonStringify);
960
+ if (nested) {
961
+ return nested;
962
+ }
963
+ }
964
+ if (allowJsonStringify) {
965
+ try {
966
+ const serialized = JSON.stringify(value, null, 2);
967
+ return serialized.trim() || null;
968
+ }
969
+ catch {
970
+ return null;
971
+ }
972
+ }
973
+ }
974
+ return null;
975
+ }
487
976
  }
488
977
  export function createRequestExecutor(deps) {
489
978
  return new HubRequestExecutor(deps);