@jsonstudio/rcc 0.89.3 → 0.89.168

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 (284) hide show
  1. package/README.md +240 -179
  2. package/config/modules.json +1 -11
  3. package/dist/build-info.js +3 -3
  4. package/dist/build-info.js.map +1 -1
  5. package/dist/client/gemini-cli/gemini-cli-protocol-client.d.ts +16 -0
  6. package/dist/client/gemini-cli/gemini-cli-protocol-client.js +56 -0
  7. package/dist/client/gemini-cli/gemini-cli-protocol-client.js.map +1 -0
  8. package/dist/client/openai/chat-protocol-client.js.map +1 -1
  9. package/dist/config/modules.json +1 -11
  10. package/dist/core/provider-health-manager.d.ts +17 -0
  11. package/dist/core/provider-health-manager.js +66 -0
  12. package/dist/core/provider-health-manager.js.map +1 -0
  13. package/dist/error-handling/route-error-hub.d.ts +48 -0
  14. package/dist/error-handling/route-error-hub.js +131 -0
  15. package/dist/error-handling/route-error-hub.js.map +1 -0
  16. package/dist/index.js +26 -1
  17. package/dist/index.js.map +1 -1
  18. package/dist/modules/llmswitch/bridge.d.ts +2 -0
  19. package/dist/modules/llmswitch/bridge.js +17 -0
  20. package/dist/modules/llmswitch/bridge.js.map +1 -1
  21. package/dist/modules/pipeline/utils/colored-logger.d.ts +14 -0
  22. package/dist/modules/pipeline/utils/colored-logger.js +48 -0
  23. package/dist/modules/pipeline/utils/colored-logger.js.map +1 -0
  24. package/dist/modules/pipeline/utils/debug-logger.d.ts +2 -0
  25. package/dist/modules/pipeline/utils/debug-logger.js +36 -0
  26. package/dist/modules/pipeline/utils/debug-logger.js.map +1 -1
  27. package/dist/providers/auth/gemini-cli-userinfo-helper.d.ts +53 -0
  28. package/dist/providers/auth/gemini-cli-userinfo-helper.js +152 -0
  29. package/dist/providers/auth/gemini-cli-userinfo-helper.js.map +1 -0
  30. package/dist/providers/auth/oauth-auth.js +3 -2
  31. package/dist/providers/auth/oauth-auth.js.map +1 -1
  32. package/dist/providers/auth/oauth-lifecycle.js +21 -20
  33. package/dist/providers/auth/oauth-lifecycle.js.map +1 -1
  34. package/dist/providers/auth/oauth-logger.d.ts +1 -0
  35. package/dist/providers/auth/oauth-logger.js +21 -0
  36. package/dist/providers/auth/oauth-logger.js.map +1 -0
  37. package/dist/providers/compat/compat-directory-loader.js +2 -55
  38. package/dist/providers/compat/compat-directory-loader.js.map +1 -1
  39. package/dist/providers/compat/compatibility-factory.d.ts +4 -4
  40. package/dist/providers/compat/compatibility-factory.js +108 -0
  41. package/dist/providers/compat/compatibility-factory.js.map +1 -1
  42. package/dist/providers/compat/glm/glm-compatibility.d.ts +2 -2
  43. package/dist/providers/compat/glm/glm-compatibility.js +7 -7
  44. package/dist/providers/compat/glm/glm-compatibility.js.map +1 -1
  45. package/dist/providers/compat/glm/index.js +0 -6
  46. package/dist/providers/compat/glm/index.js.map +1 -1
  47. package/dist/providers/compat/iflow/iflow-compatibility.d.ts +1 -1
  48. package/dist/providers/compat/iflow/iflow-compatibility.js +6 -6
  49. package/dist/providers/compat/iflow/iflow-compatibility.js.map +1 -1
  50. package/dist/providers/compat/index.d.ts +0 -6
  51. package/dist/providers/compat/index.js +0 -7
  52. package/dist/providers/compat/index.js.map +1 -1
  53. package/dist/providers/compat/lmstudio-compatibility.d.ts +2 -2
  54. package/dist/providers/compat/lmstudio-compatibility.js +4 -4
  55. package/dist/providers/compat/lmstudio-compatibility.js.map +1 -1
  56. package/dist/providers/compat/passthrough-compatibility.d.ts +1 -1
  57. package/dist/providers/compat/passthrough-compatibility.js +3 -3
  58. package/dist/providers/compat/passthrough-compatibility.js.map +1 -1
  59. package/dist/providers/compat/profiles/chat/glm/index.d.ts +6 -0
  60. package/dist/providers/compat/profiles/chat/glm/index.js +6 -0
  61. package/dist/providers/compat/profiles/chat/glm/index.js.map +1 -0
  62. package/dist/providers/compat/profiles/chat/iflow/index.d.ts +6 -0
  63. package/dist/providers/compat/profiles/chat/iflow/index.js +6 -0
  64. package/dist/providers/compat/profiles/chat/iflow/index.js.map +1 -0
  65. package/dist/providers/compat/profiles/chat/lmstudio/index.d.ts +6 -0
  66. package/dist/providers/compat/profiles/chat/lmstudio/index.js +6 -0
  67. package/dist/providers/compat/profiles/chat/lmstudio/index.js.map +1 -0
  68. package/dist/providers/compat/profiles/chat/qwen/index.d.ts +6 -0
  69. package/dist/providers/compat/profiles/chat/qwen/index.js +6 -0
  70. package/dist/providers/compat/profiles/chat/qwen/index.js.map +1 -0
  71. package/dist/providers/compat/profiles/compat/passthrough/index.d.ts +6 -0
  72. package/dist/providers/compat/profiles/compat/passthrough/index.js +6 -0
  73. package/dist/providers/compat/profiles/compat/passthrough/index.js.map +1 -0
  74. package/dist/providers/compat/profiles/responses/c4m/index.d.ts +6 -0
  75. package/dist/providers/compat/profiles/responses/c4m/index.js +6 -0
  76. package/dist/providers/compat/profiles/responses/c4m/index.js.map +1 -0
  77. package/dist/providers/compat/profiles/responses/default/index.d.ts +6 -0
  78. package/dist/providers/compat/profiles/responses/default/index.js +6 -0
  79. package/dist/providers/compat/profiles/responses/default/index.js.map +1 -0
  80. package/dist/providers/compat/profiles/responses/fai/index.d.ts +6 -0
  81. package/dist/providers/compat/profiles/responses/fai/index.js +6 -0
  82. package/dist/providers/compat/profiles/responses/fai/index.js.map +1 -0
  83. package/dist/providers/compat/profiles/responses/fc/index.d.ts +6 -0
  84. package/dist/providers/compat/profiles/responses/fc/index.js +6 -0
  85. package/dist/providers/compat/profiles/responses/fc/index.js.map +1 -0
  86. package/dist/providers/compat/qwen/index.js +0 -6
  87. package/dist/providers/compat/qwen/index.js.map +1 -1
  88. package/dist/providers/compat/qwen-compatibility.d.ts +2 -2
  89. package/dist/providers/compat/qwen-compatibility.js +4 -4
  90. package/dist/providers/compat/qwen-compatibility.js.map +1 -1
  91. package/dist/providers/compat/register-compat-module.d.ts +8 -0
  92. package/dist/providers/compat/register-compat-module.js +53 -0
  93. package/dist/providers/compat/register-compat-module.js.map +1 -0
  94. package/dist/providers/compat/responses/c4m-responses-compatibility.d.ts +6 -2
  95. package/dist/providers/compat/responses/c4m-responses-compatibility.js +85 -3
  96. package/dist/providers/compat/responses/c4m-responses-compatibility.js.map +1 -1
  97. package/dist/providers/compat/standard-compatibility-utils.js +45 -15
  98. package/dist/providers/compat/standard-compatibility-utils.js.map +1 -1
  99. package/dist/providers/core/api/provider-config.d.ts +1 -1
  100. package/dist/providers/core/api/provider-types.d.ts +3 -1
  101. package/dist/providers/core/api/provider-types.js +1 -0
  102. package/dist/providers/core/api/provider-types.js.map +1 -1
  103. package/dist/providers/core/config/service-profiles.js +19 -2
  104. package/dist/providers/core/config/service-profiles.js.map +1 -1
  105. package/dist/providers/core/runtime/base-provider.d.ts +8 -0
  106. package/dist/providers/core/runtime/base-provider.js +79 -36
  107. package/dist/providers/core/runtime/base-provider.js.map +1 -1
  108. package/dist/providers/core/runtime/gemini-cli-http-provider.d.ts +33 -0
  109. package/dist/providers/core/runtime/gemini-cli-http-provider.js +156 -0
  110. package/dist/providers/core/runtime/gemini-cli-http-provider.js.map +1 -0
  111. package/dist/providers/core/runtime/gemini-http-provider.d.ts +1 -2
  112. package/dist/providers/core/runtime/gemini-http-provider.js +0 -12
  113. package/dist/providers/core/runtime/gemini-http-provider.js.map +1 -1
  114. package/dist/providers/core/runtime/http-request-executor.d.ts +42 -0
  115. package/dist/providers/core/runtime/http-request-executor.js +133 -0
  116. package/dist/providers/core/runtime/http-request-executor.js.map +1 -0
  117. package/dist/providers/core/runtime/http-transport-provider.d.ts +7 -11
  118. package/dist/providers/core/runtime/http-transport-provider.js +226 -371
  119. package/dist/providers/core/runtime/http-transport-provider.js.map +1 -1
  120. package/dist/providers/core/runtime/provider-error-classifier.d.ts +25 -0
  121. package/dist/providers/core/runtime/provider-error-classifier.js +139 -0
  122. package/dist/providers/core/runtime/provider-error-classifier.js.map +1 -0
  123. package/dist/providers/core/runtime/provider-error-types.d.ts +23 -0
  124. package/dist/providers/core/runtime/provider-error-types.js +2 -0
  125. package/dist/providers/core/runtime/provider-error-types.js.map +1 -0
  126. package/dist/providers/core/runtime/provider-factory.d.ts +1 -1
  127. package/dist/providers/core/runtime/provider-factory.js +14 -0
  128. package/dist/providers/core/runtime/provider-factory.js.map +1 -1
  129. package/dist/providers/core/runtime/provider-runtime-metadata.d.ts +1 -0
  130. package/dist/providers/core/runtime/provider-runtime-metadata.js.map +1 -1
  131. package/dist/providers/core/runtime/responses-provider.d.ts +7 -0
  132. package/dist/providers/core/runtime/responses-provider.js +228 -12
  133. package/dist/providers/core/runtime/responses-provider.js.map +1 -1
  134. package/dist/providers/core/strategies/oauth-auth-code-flow.js +10 -9
  135. package/dist/providers/core/strategies/oauth-auth-code-flow.js.map +1 -1
  136. package/dist/providers/core/strategies/oauth-device-flow.js +10 -9
  137. package/dist/providers/core/strategies/oauth-device-flow.js.map +1 -1
  138. package/dist/providers/core/utils/provider-error-reporter.js +63 -15
  139. package/dist/providers/core/utils/provider-error-reporter.js.map +1 -1
  140. package/dist/providers/core/utils/provider-type-utils.d.ts +1 -1
  141. package/dist/providers/core/utils/provider-type-utils.js +6 -1
  142. package/dist/providers/core/utils/provider-type-utils.js.map +1 -1
  143. package/dist/providers/core/utils/snapshot-writer.d.ts +10 -0
  144. package/dist/providers/core/utils/snapshot-writer.js +85 -0
  145. package/dist/providers/core/utils/snapshot-writer.js.map +1 -1
  146. package/dist/providers/mock/mock-provider-runtime.js +44 -0
  147. package/dist/providers/mock/mock-provider-runtime.js.map +1 -1
  148. package/dist/providers/profile/provider-profile-loader.js +26 -19
  149. package/dist/providers/profile/provider-profile-loader.js.map +1 -1
  150. package/dist/providers/profile/provider-profile.d.ts +2 -2
  151. package/dist/server/handlers/chat-handler.js +9 -3
  152. package/dist/server/handlers/chat-handler.js.map +1 -1
  153. package/dist/server/handlers/handler-utils.d.ts +7 -1
  154. package/dist/server/handlers/handler-utils.js +64 -52
  155. package/dist/server/handlers/handler-utils.js.map +1 -1
  156. package/dist/server/handlers/messages-handler.js +9 -3
  157. package/dist/server/handlers/messages-handler.js.map +1 -1
  158. package/dist/server/handlers/responses-handler.js +21 -13
  159. package/dist/server/handlers/responses-handler.js.map +1 -1
  160. package/dist/server/runtime/http-server/colored-logger.d.ts +1 -0
  161. package/dist/server/runtime/http-server/colored-logger.js +33 -0
  162. package/dist/server/runtime/http-server/colored-logger.js.map +1 -0
  163. package/dist/server/runtime/http-server/index.d.ts +3 -0
  164. package/dist/server/runtime/http-server/index.js +76 -19
  165. package/dist/server/runtime/http-server/index.js.map +1 -1
  166. package/dist/server/runtime/http-server/provider-utils.d.ts +3 -1
  167. package/dist/server/runtime/http-server/provider-utils.js +12 -2
  168. package/dist/server/runtime/http-server/provider-utils.js.map +1 -1
  169. package/dist/server/runtime/http-server/request-executor.js +6 -2
  170. package/dist/server/runtime/http-server/request-executor.js.map +1 -1
  171. package/dist/server/runtime/http-server/routes.js +31 -11
  172. package/dist/server/runtime/http-server/routes.js.map +1 -1
  173. package/dist/server/runtime/http-server/types.d.ts +2 -1
  174. package/dist/utils/error-center-payload.d.ts +7 -0
  175. package/dist/utils/error-center-payload.js +67 -0
  176. package/dist/utils/error-center-payload.js.map +1 -0
  177. package/dist/utils/error-handler-registry.d.ts +7 -0
  178. package/dist/utils/error-handler-registry.js +44 -12
  179. package/dist/utils/error-handler-registry.js.map +1 -1
  180. package/node_modules/@jsonstudio/llms/dist/conversion/codecs/responses-openai-codec.js +16 -1
  181. package/node_modules/@jsonstudio/llms/dist/conversion/compat/profiles/chat-glm.json +17 -0
  182. package/node_modules/@jsonstudio/llms/dist/conversion/compat/profiles/chat-iflow.json +36 -0
  183. package/node_modules/@jsonstudio/llms/dist/conversion/compat/profiles/chat-lmstudio.json +37 -0
  184. package/node_modules/@jsonstudio/llms/dist/conversion/compat/profiles/chat-qwen.json +18 -0
  185. package/node_modules/@jsonstudio/llms/dist/conversion/compat/profiles/responses-c4m.json +45 -0
  186. package/node_modules/@jsonstudio/llms/dist/conversion/config/compat-profiles.json +38 -0
  187. package/node_modules/@jsonstudio/llms/dist/conversion/config/sample-config.json +314 -0
  188. package/node_modules/@jsonstudio/llms/dist/conversion/config/version-switch.json +150 -0
  189. package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/compat/compat-engine.d.ts +4 -0
  190. package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/compat/compat-engine.js +667 -0
  191. package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/compat/compat-profile-store.d.ts +2 -0
  192. package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/compat/compat-profile-store.js +76 -0
  193. package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/compat/compat-types.d.ts +62 -0
  194. package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/compat/compat-types.js +1 -0
  195. package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/hub-pipeline.d.ts +2 -0
  196. package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/hub-pipeline.js +110 -29
  197. package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage3_compat/index.d.ts +14 -0
  198. package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage3_compat/index.js +23 -0
  199. package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/stages/resp_outbound/resp_outbound_stage1_client_remap/index.js +34 -0
  200. package/node_modules/@jsonstudio/llms/dist/conversion/hub/process/chat-process.js +4 -1
  201. package/node_modules/@jsonstudio/llms/dist/conversion/hub/response/provider-response.js +26 -0
  202. package/node_modules/@jsonstudio/llms/dist/conversion/responses/responses-openai-bridge.d.ts +1 -0
  203. package/node_modules/@jsonstudio/llms/dist/conversion/responses/responses-openai-bridge.js +71 -0
  204. package/node_modules/@jsonstudio/llms/dist/conversion/shared/responses-conversation-store.d.ts +35 -0
  205. package/node_modules/@jsonstudio/llms/dist/conversion/shared/responses-conversation-store.js +64 -19
  206. package/node_modules/@jsonstudio/llms/dist/conversion/shared/tool-filter-pipeline.d.ts +21 -0
  207. package/node_modules/@jsonstudio/llms/dist/conversion/shared/tool-filter-pipeline.js +138 -22
  208. package/node_modules/@jsonstudio/llms/dist/conversion/shared/tool-governor.d.ts +21 -0
  209. package/node_modules/@jsonstudio/llms/dist/conversion/shared/tool-governor.js +116 -1
  210. package/node_modules/@jsonstudio/llms/dist/conversion/shared/tool-mapping.js +52 -2
  211. package/node_modules/@jsonstudio/llms/dist/filters/config/openai-openai.fieldmap.json +18 -0
  212. package/node_modules/@jsonstudio/llms/dist/filters/special/request-tools-normalize.js +20 -1
  213. package/node_modules/@jsonstudio/llms/dist/guidance/index.js +6 -2
  214. package/node_modules/@jsonstudio/llms/dist/router/virtual-router/bootstrap.js +16 -7
  215. package/node_modules/@jsonstudio/llms/dist/router/virtual-router/classifier.js +40 -37
  216. package/node_modules/@jsonstudio/llms/dist/router/virtual-router/default-thinking-keywords.d.ts +1 -0
  217. package/node_modules/@jsonstudio/llms/dist/router/virtual-router/default-thinking-keywords.js +13 -0
  218. package/node_modules/@jsonstudio/llms/dist/router/virtual-router/engine.d.ts +39 -0
  219. package/node_modules/@jsonstudio/llms/dist/router/virtual-router/engine.js +52 -11
  220. package/node_modules/@jsonstudio/llms/dist/router/virtual-router/features.js +340 -11
  221. package/node_modules/@jsonstudio/llms/dist/router/virtual-router/token-counter.d.ts +2 -0
  222. package/node_modules/@jsonstudio/llms/dist/router/virtual-router/token-counter.js +105 -0
  223. package/node_modules/@jsonstudio/llms/dist/router/virtual-router/types.d.ts +8 -0
  224. package/node_modules/@jsonstudio/llms/dist/router/virtual-router/types.js +2 -2
  225. package/node_modules/@jsonstudio/llms/dist/sse/sse-to-json/builders/response-builder.d.ts +2 -0
  226. package/node_modules/@jsonstudio/llms/dist/sse/sse-to-json/builders/response-builder.js +53 -11
  227. package/node_modules/@jsonstudio/llms/dist/test-output/virtual-router/results.json +1 -0
  228. package/node_modules/@jsonstudio/llms/dist/test-output/virtual-router/summary.json +12 -0
  229. package/node_modules/@jsonstudio/llms/dist/tools/tool-registry.js +4 -3
  230. package/node_modules/@jsonstudio/llms/package.json +3 -3
  231. package/package.json +13 -10
  232. package/scripts/analyze-routing-classifier.mjs +166 -0
  233. package/scripts/analyze-routing-samples.mjs +216 -0
  234. package/scripts/analyze-thinking-keywords.mjs +17 -2
  235. package/scripts/build-core.mjs +8 -0
  236. package/scripts/classify-sample-tools.mjs +252 -0
  237. package/scripts/ensure-llmswitch-mode.mjs +95 -0
  238. package/scripts/gen-build-info.mjs +58 -4
  239. package/scripts/install-global.sh +1 -1
  240. package/scripts/install-release.sh +7 -0
  241. package/scripts/mock-provider/run-regressions.mjs +60 -14
  242. package/scripts/pack-mode.mjs +30 -1
  243. package/scripts/publish-rcc.mjs +31 -0
  244. package/scripts/tests/apply-patch-loop.mjs +100 -9
  245. package/scripts/tests/golden-provider-cycle.mjs +12 -1
  246. package/scripts/tests/responses-provider-dry-run.mjs +15 -1
  247. package/scripts/tests/virtual-router-health.mjs +12 -5
  248. package/scripts/tools/capture-provider-goldens.mjs +75 -25
  249. package/scripts/tools/responses-golden-dry-run.mjs +17 -1
  250. package/scripts/tools/responses-provider-replay.mjs +17 -1
  251. package/scripts/tools/sync-ci-goldens.mjs +131 -0
  252. package/scripts/verification/samples/openai-chat-list-local-files.json +19 -796
  253. package/scripts/verify-e2e-toolcall.mjs +52 -0
  254. package/dist/providers/compat/config/index.d.ts +0 -1
  255. package/dist/providers/compat/config/index.js +0 -5
  256. package/dist/providers/compat/config/index.js.map +0 -1
  257. package/dist/providers/compat/iflow/index.d.ts +0 -27
  258. package/dist/providers/compat/iflow/index.js +0 -32
  259. package/dist/providers/compat/iflow/index.js.map +0 -1
  260. package/dist/providers/compat/lmstudio/index.d.ts +0 -4
  261. package/dist/providers/compat/lmstudio/index.js +0 -10
  262. package/dist/providers/compat/lmstudio/index.js.map +0 -1
  263. package/dist/providers/compat/passthrough/index.d.ts +0 -4
  264. package/dist/providers/compat/passthrough/index.js +0 -9
  265. package/dist/providers/compat/passthrough/index.js.map +0 -1
  266. package/dist/providers/compat/responses/index.d.ts +0 -1
  267. package/dist/providers/compat/responses/index.js +0 -8
  268. package/dist/providers/compat/responses/index.js.map +0 -1
  269. package/dist/providers/core/composite/compat/anthropic.d.ts +0 -3
  270. package/dist/providers/core/composite/compat/anthropic.js +0 -7
  271. package/dist/providers/core/composite/compat/anthropic.js.map +0 -1
  272. package/dist/providers/core/composite/compat/gemini.d.ts +0 -3
  273. package/dist/providers/core/composite/compat/gemini.js +0 -7
  274. package/dist/providers/core/composite/compat/gemini.js.map +0 -1
  275. package/dist/providers/core/composite/compat/openai-compat-aggregator.d.ts +0 -9
  276. package/dist/providers/core/composite/compat/openai-compat-aggregator.js +0 -135
  277. package/dist/providers/core/composite/compat/openai-compat-aggregator.js.map +0 -1
  278. package/dist/providers/core/composite/compat/responses.d.ts +0 -3
  279. package/dist/providers/core/composite/compat/responses.js +0 -91
  280. package/dist/providers/core/composite/compat/responses.js.map +0 -1
  281. package/dist/providers/core/composite/provider-composite.d.ts +0 -50
  282. package/dist/providers/core/composite/provider-composite.js +0 -235
  283. package/dist/providers/core/composite/provider-composite.js.map +0 -1
  284. package/scripts/tests/apply-patch-loop.mjs.bak +0 -363
@@ -13,21 +13,23 @@ import { HttpClient } from '../utils/http-client.js';
13
13
  import { DynamicProfileLoader, ServiceProfileValidator } from '../config/service-profiles.js';
14
14
  import { ApiKeyAuthProvider } from '../../auth/apikey-auth.js';
15
15
  import { OAuthAuthProvider } from '../../auth/oauth-auth.js';
16
+ import { logOAuthDebug } from '../../auth/oauth-logger.js';
16
17
  import { TokenFileAuthProvider } from '../../auth/tokenfile-auth.js';
17
18
  import { ensureValidOAuthToken, handleUpstreamInvalidOAuthToken } from '../../auth/oauth-lifecycle.js';
18
- import { createHookSystemIntegration, HookSystemIntegration } from '../hooks/hooks-integration.js';
19
- import { writeProviderSnapshot } from '../utils/snapshot-writer.js';
20
- import { ProviderComposite } from '../composite/provider-composite.js';
19
+ import { attachProviderSseSnapshotStream, writeProviderSnapshot } from '../utils/snapshot-writer.js';
21
20
  import { attachProviderRuntimeMetadata } from './provider-runtime-metadata.js';
22
21
  import { OpenAIChatProtocolClient } from '../../../client/openai/chat-protocol-client.js';
22
+ import { HttpRequestExecutor } from './http-request-executor.js';
23
+ import { extractStatusCodeFromError } from './provider-error-classifier.js';
23
24
  const isRecord = (value) => typeof value === 'object' && value !== null;
25
+ const DEFAULT_USER_AGENT = 'RouteCodex/2.0';
24
26
  export class HttpTransportProvider extends BaseProvider {
25
27
  type;
26
28
  authProvider = null;
27
29
  httpClient;
28
30
  serviceProfile;
29
- hookSystemIntegration;
30
31
  protocolClient;
32
+ requestExecutor;
31
33
  injectedConfig = null;
32
34
  constructor(config, dependencies, moduleType, protocolClient) {
33
35
  super(config, dependencies);
@@ -39,10 +41,9 @@ export class HttpTransportProvider extends BaseProvider {
39
41
  this.validateConfig();
40
42
  // 创建HTTP客户端
41
43
  this.createHttpClient();
44
+ this.requestExecutor = new HttpRequestExecutor(this.httpClient, this.createRequestExecutorDeps());
42
45
  // 创建认证提供者
43
46
  this.authProvider = this.createAuthProvider();
44
- // 初始化Hook系统集成
45
- this.hookSystemIntegration = this.initializeHookSystem();
46
47
  }
47
48
  /**
48
49
  * 确保认证提供者完成初始化(避免 ApiKeyAuthProvider 未初始化导致的报错)
@@ -54,12 +55,13 @@ export class HttpTransportProvider extends BaseProvider {
54
55
  const providerConfig = this.config.config;
55
56
  const extensions = this.getConfigExtensions();
56
57
  const auth = providerConfig.auth;
57
- if (this.normalizeAuthMode(auth.type) === 'oauth') {
58
+ const usesTokenFile = this.authProvider instanceof TokenFileAuthProvider;
59
+ if (this.normalizeAuthMode(auth.type) === 'oauth' && !usesTokenFile) {
58
60
  const oauthAuth = auth;
59
61
  const oauthProviderId = this.ensureOAuthProviderId(oauthAuth, extensions);
60
62
  const forceReauthorize = false;
61
63
  const tokenFileHint = oauthAuth.tokenFile ?? '(default)';
62
- console.log(`[OAuth] [init] provider=${oauthProviderId} type=${auth.type} tokenFile=${tokenFileHint} forceReauth=${forceReauthorize}`);
64
+ logOAuthDebug(`[OAuth] [init] provider=${oauthProviderId} type=${auth.type} tokenFile=${tokenFileHint} forceReauth=${forceReauthorize}`);
63
65
  this.dependencies.logger?.logModule?.(this.id, 'oauth-init-start', {
64
66
  providerType: oauthProviderId,
65
67
  tokenFile: tokenFileHint,
@@ -71,7 +73,7 @@ export class HttpTransportProvider extends BaseProvider {
71
73
  openBrowser: true,
72
74
  forceReauthorize
73
75
  });
74
- console.log('[OAuth] [init] ensureValid OK');
76
+ logOAuthDebug('[OAuth] [init] ensureValid OK');
75
77
  try {
76
78
  if (this.authProvider instanceof TokenFileAuthProvider) {
77
79
  await this.authProvider.initialize();
@@ -113,14 +115,6 @@ export class HttpTransportProvider extends BaseProvider {
113
115
  }
114
116
  }
115
117
  }
116
- // 初始化新的Hook系统集成
117
- await this.hookSystemIntegration.initialize();
118
- // 设置调试配置(向后兼容)
119
- this.configureHookDebugging();
120
- this.dependencies.logger?.logModule(this.id, 'provider-hook-system-initialized', {
121
- providerType: this.providerType,
122
- integrationEnabled: true
123
- });
124
118
  }
125
119
  catch (error) {
126
120
  // 暴露问题,快速失败,便于定位凭证问题
@@ -150,83 +144,6 @@ export class HttpTransportProvider extends BaseProvider {
150
144
  getConfig() {
151
145
  return this.injectedConfig ?? this.config.config ?? null;
152
146
  }
153
- /**
154
- * 初始化Hook系统集成
155
- */
156
- initializeHookSystem() {
157
- try {
158
- const integration = createHookSystemIntegration(this.dependencies, this.id, {
159
- enabled: true,
160
- debugMode: true, // Provider v2默认启用调试模式
161
- snapshotEnabled: true,
162
- migrationMode: true // 迁移现有Hooks
163
- });
164
- this.dependencies.logger?.logModule(this.id, 'hook-system-integration-created', {
165
- providerId: this.id
166
- });
167
- return integration;
168
- }
169
- catch (error) {
170
- this.dependencies.logger?.logModule(this.id, 'hook-system-integration-failed', {
171
- error: error instanceof Error ? error.message : String(error)
172
- });
173
- // 创建兼容的空实现,避免系统崩溃
174
- return {
175
- getBidirectionalHookManager: () => ({
176
- registerHook: () => { },
177
- unregisterHook: () => { },
178
- executeHookChain: async () => ({ data: {}, metrics: {} }),
179
- setDebugConfig: () => { }
180
- }),
181
- setDebugConfig: () => { },
182
- initialize: async () => { },
183
- getStats: () => ({ enabled: false }),
184
- healthCheck: async () => ({ healthy: true }),
185
- start: async () => { },
186
- stop: async () => { },
187
- shutdown: async () => { }
188
- };
189
- }
190
- }
191
- /**
192
- * 配置Hook调试(保持向后兼容)
193
- */
194
- configureHookDebugging() {
195
- try {
196
- // 设置调试配置(使用统一Hook系统的阶段字符串)
197
- const debugConfig = {
198
- enabled: true,
199
- level: 'verbose',
200
- maxDataSize: 1024 * 64, // 64KB 单次输出上限,避免过大控制台噪声
201
- stages: [
202
- 'request_preprocessing',
203
- 'request_validation',
204
- 'authentication',
205
- 'http_request',
206
- 'http_response',
207
- 'response_validation',
208
- 'response_postprocessing',
209
- 'error_handling'
210
- ],
211
- outputFormat: 'structured',
212
- outputTargets: ['console'],
213
- performanceThresholds: {
214
- maxHookExecutionTime: 500, // 单个Hook 500ms告警
215
- maxTotalExecutionTime: 5000, // 阶段总时长 5s 告警
216
- maxDataSize: 1024 * 256 // 256KB 数据告警
217
- }
218
- };
219
- this.hookSystemIntegration.setDebugConfig(debugConfig);
220
- this.dependencies.logger?.logModule(this.id, 'provider-debug-hooks-configured', {
221
- providerType: this.providerType
222
- });
223
- }
224
- catch (error) {
225
- this.dependencies.logger?.logModule(this.id, 'provider-debug-hooks-error', {
226
- error: error instanceof Error ? error.message : String(error)
227
- });
228
- }
229
- }
230
147
  getServiceProfile() {
231
148
  const cfg = this.config.config;
232
149
  const profileKey = this.resolveProfileKey(cfg);
@@ -320,10 +237,16 @@ export class HttpTransportProvider extends BaseProvider {
320
237
  const auth = this.config.config.auth;
321
238
  const extensions = this.getConfigExtensions();
322
239
  const authMode = this.normalizeAuthMode(auth.type);
323
- const providerIdForAuth = authMode === 'oauth'
240
+ this.authMode = authMode;
241
+ let providerIdForAuth = authMode === 'oauth'
324
242
  ? this.ensureOAuthProviderId(auth, extensions)
325
243
  : this.providerType;
326
- // 验证认证配置(按 providerIdForAuth 选择服务档案)
244
+ if (this.type === 'gemini-cli-http-provider') {
245
+ providerIdForAuth = 'gemini-cli';
246
+ }
247
+ if (authMode === 'oauth') {
248
+ this.oauthProviderId = providerIdForAuth;
249
+ }
327
250
  const validation = ServiceProfileValidator.validateServiceProfile(providerIdForAuth, authMode);
328
251
  if (!validation.isValid) {
329
252
  throw new Error(`Invalid auth configuration for ${providerIdForAuth}: ${validation.errors.join(', ')}`);
@@ -334,9 +257,9 @@ export class HttpTransportProvider extends BaseProvider {
334
257
  }
335
258
  else if (authMode === 'oauth') {
336
259
  const oauthAuth = auth;
337
- // For providers like Qwen where public OAuth client may not be available,
338
- // allow reading tokens produced by external login tools (CLIProxyAPI)
339
- const useTokenFile = (providerIdForAuth === 'qwen' || providerIdForAuth === 'iflow') &&
260
+ // For providers like Qwen/iflow/Gemini CLI where public OAuth client may not be available,
261
+ // allow reading tokens produced by external login tools (CLIProxyAPI) via token file.
262
+ const useTokenFile = (providerIdForAuth === 'qwen' || providerIdForAuth === 'iflow' || providerIdForAuth === 'gemini-cli') &&
340
263
  !oauthAuth.clientId &&
341
264
  !oauthAuth.tokenUrl &&
342
265
  !oauthAuth.deviceCodeUrl;
@@ -374,6 +297,27 @@ export class HttpTransportProvider extends BaseProvider {
374
297
  }
375
298
  });
376
299
  }
300
+ createRequestExecutorDeps() {
301
+ return {
302
+ wantsUpstreamSse: this.wantsUpstreamSse.bind(this),
303
+ getEffectiveEndpoint: () => this.getEffectiveEndpoint(),
304
+ resolveRequestEndpoint: this.resolveRequestEndpoint.bind(this),
305
+ buildRequestHeaders: this.buildRequestHeaders.bind(this),
306
+ finalizeRequestHeaders: this.finalizeRequestHeaders.bind(this),
307
+ applyStreamModeHeaders: this.applyStreamModeHeaders.bind(this),
308
+ getEffectiveBaseUrl: () => this.getEffectiveBaseUrl(),
309
+ buildHttpRequestBody: this.buildHttpRequestBody.bind(this),
310
+ prepareSseRequestBody: this.prepareSseRequestBody.bind(this),
311
+ getEntryEndpointFromPayload: this.getEntryEndpointFromPayload.bind(this),
312
+ getClientRequestIdFromContext: this.getClientRequestIdFromContext.bind(this),
313
+ wrapUpstreamSseResponse: this.wrapUpstreamSseResponse.bind(this),
314
+ getHttpRetryLimit: () => this.getHttpRetryLimit(),
315
+ shouldRetryHttpError: this.shouldRetryHttpError.bind(this),
316
+ delayBeforeHttpRetry: this.delayBeforeHttpRetry.bind(this),
317
+ tryRecoverOAuthAndReplay: this.tryRecoverOAuthAndReplay.bind(this),
318
+ normalizeHttpError: this.normalizeHttpError.bind(this)
319
+ };
320
+ }
377
321
  async preprocessRequest(request) {
378
322
  const context = this.createProviderContext();
379
323
  const runtimeMetadata = context.runtimeMetadata;
@@ -416,89 +360,19 @@ export class HttpTransportProvider extends BaseProvider {
416
360
  }
417
361
  }
418
362
  catch { /* ignore */ }
419
- // 获取Hook管理器(新的统一系统)
420
- const hookManager = this.getHookManager();
421
- // 🔍 Hook 1: 请求预处理阶段
422
- const preprocessResult = await hookManager.executeHookChain('request_preprocessing', 'request', processedRequest, context);
423
- processedRequest = preprocessResult.data;
424
- ensureRuntimeMetadata(processedRequest);
425
- // 🔍 Hook 2: 请求验证阶段
426
- const validationResult = await hookManager.executeHookChain('request_validation', 'request', processedRequest, context);
427
- processedRequest = validationResult.data;
428
- ensureRuntimeMetadata(processedRequest);
429
- // Provider 层不再修改工具 schema;统一入口在 llmswitch-core/兼容层
430
- // 新增:ProviderComposite.compat.request(协议敏感;Fail Fast)
431
- try {
432
- const compatProfile = (runtime?.compatibilityProfile || '').toLowerCase();
433
- const shouldRunCompat = compatProfile !== 'none';
434
- if (shouldRunCompat) {
435
- ensureRuntimeMetadata(processedRequest);
436
- processedRequest = await ProviderComposite.applyRequest(processedRequest, {
437
- providerType: runtime?.providerType || this.providerType,
438
- providerFamily: runtime?.providerFamily || runtime?.providerId || runtime?.providerKey,
439
- dependencies: this.dependencies
440
- });
441
- ensureRuntimeMetadata(processedRequest);
442
- }
443
- }
444
- catch (e) {
445
- // 暴露问题,不兜底
446
- this.dependencies.logger?.logModule?.(this.id, 'compat-request-error', {
447
- error: e instanceof Error ? e.message : String(e)
448
- });
449
- throw e;
450
- }
451
363
  return processedRequest;
452
364
  }
453
365
  async postprocessResponse(response, context) {
454
366
  const runtime = this.getRuntimeProfile();
455
- // 流式短路:若上游仍返回 SSE,则统一包装为 __sse_responses,交由 HTTP 层原样透传
456
- try {
457
- const responseRecord = this.asResponseRecord(response);
458
- if (responseRecord.__sse_stream) {
459
- return { __sse_responses: responseRecord.__sse_stream };
460
- }
461
- if (responseRecord.data?.__sse_stream) {
462
- return { __sse_responses: responseRecord.data.__sse_stream };
463
- }
464
- }
465
- catch {
466
- // ignore
467
- }
468
367
  const processingTime = Date.now() - context.startTime;
469
368
  let processedResponse = response;
470
- // 获取Hook管理器(新的统一系统)
471
- const hookManager = this.getHookManager();
472
- // 🔍 Hook 3: HTTP响应阶段
473
- const httpResponseResult = await hookManager.executeHookChain('http_response', 'response', processedResponse, context);
474
- processedResponse = httpResponseResult.data;
475
- // 🔍 Hook 4: 响应验证阶段
476
- const validationResult = await hookManager.executeHookChain('response_validation', 'response', processedResponse, context);
477
- processedResponse = validationResult.data;
478
- // 🔍 Hook 5: 响应后处理阶段
479
- const postprocessResult = await hookManager.executeHookChain('response_postprocessing', 'response', processedResponse, context);
480
- processedResponse = postprocessResult.data;
481
- // 新增:ProviderComposite.compat.response(在封装/模型名还原之前)
482
- try {
483
- const compatProfile = (runtime?.compatibilityProfile || '').toLowerCase();
484
- const shouldRunCompat = compatProfile !== 'none';
485
- if (shouldRunCompat) {
486
- processedResponse = await ProviderComposite.applyResponse(processedResponse, undefined, {
487
- providerType: runtime?.providerType || this.providerType,
488
- providerFamily: runtime?.providerFamily || runtime?.providerId || runtime?.providerKey,
489
- dependencies: this.dependencies,
490
- runtime: context.runtimeMetadata
491
- });
492
- }
493
- }
494
- catch (e) {
495
- this.dependencies.logger?.logModule?.(this.id, 'compat-response-error', {
496
- error: e instanceof Error ? e.message : String(e)
497
- });
498
- throw e;
499
- }
500
- const processedRecord = this.asResponseRecord(processedResponse);
501
369
  const originalRecord = this.asResponseRecord(response);
370
+ const processedRecord = this.asResponseRecord(processedResponse);
371
+ const sseStream = processedRecord.__sse_responses ||
372
+ processedRecord.data?.__sse_responses;
373
+ if (sseStream) {
374
+ return { __sse_responses: sseStream };
375
+ }
502
376
  return {
503
377
  data: processedRecord.data || processedResponse,
504
378
  status: processedRecord.status ?? originalRecord.status,
@@ -509,195 +383,13 @@ export class HttpTransportProvider extends BaseProvider {
509
383
  providerType: this.providerType,
510
384
  // 对外暴露的 model 统一为入站模型
511
385
  model: context.model ?? this.extractModel(processedRecord) ?? this.extractModel(originalRecord),
512
- usage: this.extractUsage(processedRecord) ?? this.extractUsage(originalRecord),
513
- hookMetrics: {
514
- httpResponse: httpResponseResult.metrics,
515
- validation: validationResult.metrics,
516
- postprocess: postprocessResult.metrics
517
- }
386
+ usage: this.extractUsage(processedRecord) ?? this.extractUsage(originalRecord)
518
387
  }
519
388
  };
520
389
  }
521
390
  async sendRequestInternal(request) {
522
391
  const context = this.createProviderContext();
523
- // 获取Hook管理器(新的统一系统)
524
- const hookManager = this.getHookManager();
525
- // 🔍 Hook 8: HTTP请求阶段
526
- const httpRequestResult = await hookManager.executeHookChain('http_request', 'request', request, context);
527
- const processedRequest = httpRequestResult.data;
528
- const wantsSse = this.wantsUpstreamSse(processedRequest, context);
529
- // 仅传入 endpoint,让 HttpClient 按 baseUrl 进行拼接;避免 full URL 再次拼接导致 /https:/ 重复
530
- const defaultEndpoint = this.getEffectiveEndpoint();
531
- const endpoint = this.resolveRequestEndpoint(processedRequest, defaultEndpoint);
532
- const headers = await this.buildRequestHeaders();
533
- let finalHeaders = await this.finalizeRequestHeaders(headers, processedRequest);
534
- finalHeaders = this.applyStreamModeHeaders(finalHeaders, wantsSse);
535
- const targetUrl = `${this.getEffectiveBaseUrl().replace(/\/$/, '')}/${endpoint.startsWith('/') ? endpoint.slice(1) : endpoint}`;
536
- // Flatten request body to standard OpenAI Chat JSON
537
- const finalBody = this.buildHttpRequestBody(processedRequest);
538
- if (wantsSse) {
539
- this.prepareSseRequestBody(finalBody, context);
540
- }
541
- const entryEndpoint = this.getEntryEndpointFromPayload(processedRequest);
542
- const clientRequestId = this.getClientRequestIdFromContext(context);
543
- // 快照:provider-request(默认开启,脱敏headers)
544
- try {
545
- await writeProviderSnapshot({
546
- phase: 'provider-request',
547
- requestId: context.requestId,
548
- data: finalBody,
549
- headers: finalHeaders,
550
- url: targetUrl,
551
- entryEndpoint,
552
- clientRequestId
553
- });
554
- }
555
- catch { /* non-blocking */ }
556
- // 发送HTTP请求(根据是否需要 SSE 决定传输模式)
557
- let response;
558
- try {
559
- if (wantsSse) {
560
- const stream = await this.httpClient.postStream(endpoint, finalBody, finalHeaders);
561
- response = await this.wrapUpstreamSseResponse(stream, context);
562
- try {
563
- await writeProviderSnapshot({
564
- phase: 'provider-response',
565
- requestId: context.requestId,
566
- data: { mode: 'sse' },
567
- headers: finalHeaders,
568
- url: targetUrl,
569
- entryEndpoint,
570
- clientRequestId
571
- });
572
- }
573
- catch { /* non-blocking */ }
574
- }
575
- else {
576
- response = await this.httpClient.post(endpoint, finalBody, finalHeaders);
577
- try {
578
- await writeProviderSnapshot({
579
- phase: 'provider-response',
580
- requestId: context.requestId,
581
- data: response,
582
- headers: finalHeaders,
583
- url: targetUrl,
584
- entryEndpoint,
585
- clientRequestId
586
- });
587
- }
588
- catch { /* non-blocking */ }
589
- }
590
- }
591
- catch (error) {
592
- // OAuth token 失效:尝试刷新/重获并重试一次
593
- try {
594
- const providerAuth = this.config.config.auth;
595
- if (this.normalizeAuthMode(providerAuth.type) === 'oauth') {
596
- const shouldRetry = await handleUpstreamInvalidOAuthToken(this.providerType, providerAuth, error);
597
- if (shouldRetry) {
598
- const retryHeaders = await this.buildRequestHeaders();
599
- let finalRetryHeaders = await this.finalizeRequestHeaders(retryHeaders, processedRequest);
600
- finalRetryHeaders = this.applyStreamModeHeaders(finalRetryHeaders, wantsSse);
601
- if (wantsSse) {
602
- const stream = await this.httpClient.postStream(endpoint, finalBody, finalRetryHeaders);
603
- const wrapped = await this.wrapUpstreamSseResponse(stream, context);
604
- try {
605
- await writeProviderSnapshot({
606
- phase: 'provider-response',
607
- requestId: context.requestId,
608
- data: { mode: 'sse', retry: true },
609
- headers: finalRetryHeaders,
610
- url: targetUrl,
611
- entryEndpoint,
612
- clientRequestId
613
- });
614
- }
615
- catch { /* non-blocking */ }
616
- return wrapped;
617
- }
618
- response = await this.httpClient.post(endpoint, finalBody, finalRetryHeaders);
619
- try {
620
- await writeProviderSnapshot({
621
- phase: 'provider-response',
622
- requestId: context.requestId,
623
- data: response,
624
- headers: finalRetryHeaders,
625
- url: targetUrl,
626
- entryEndpoint,
627
- clientRequestId
628
- });
629
- }
630
- catch { /* non-blocking */ }
631
- return response;
632
- }
633
- }
634
- }
635
- catch { /* ignore and fallthrough */ }
636
- // 🔍 Hook 9: 错误处理阶段
637
- const errorResult = await hookManager.executeHookChain('error_handling', 'error', { error, request: processedRequest, url: targetUrl, headers: finalHeaders }, context);
638
- // 如果Hook处理了错误,使用Hook的返回结果
639
- const hookErrorData = errorResult.data;
640
- if (hookErrorData && hookErrorData.error === false) {
641
- return hookErrorData;
642
- }
643
- // 规范化错误:补充结构化字段,移除仅文本填充的旧做法
644
- const normalized = error;
645
- try {
646
- // 提取状态码
647
- const msg = typeof normalized.message === 'string' ? normalized.message : String(normalized || '');
648
- const m = msg.match(/HTTP\s+(\d{3})/i);
649
- const parsedStatus = m ? parseInt(m[1], 10) : undefined;
650
- const statusCode = Number.isFinite(normalized.statusCode)
651
- ? Number(normalized.statusCode)
652
- : (Number.isFinite(normalized.status) ? Number(normalized.status) : (parsedStatus || undefined));
653
- if (statusCode && !Number.isNaN(statusCode)) {
654
- normalized.statusCode = statusCode;
655
- if (!normalized.status) {
656
- normalized.status = statusCode;
657
- }
658
- if (!normalized.code) {
659
- normalized.code = `HTTP_${statusCode}`;
660
- }
661
- }
662
- // 兼容 Manager 的 code 路径(response.data.error.code)
663
- if (!normalized.response) {
664
- normalized.response = {};
665
- }
666
- if (!normalized.response.data) {
667
- normalized.response.data = {};
668
- }
669
- if (!normalized.response.data.error) {
670
- normalized.response.data.error = {};
671
- }
672
- if (normalized.code && !normalized.response.data.error.code) {
673
- normalized.response.data.error.code = normalized.code;
674
- }
675
- }
676
- catch { /* keep original */ }
677
- // 快照:provider-error(结构化写入)
678
- try {
679
- await writeProviderSnapshot({
680
- phase: 'provider-error',
681
- requestId: context.requestId,
682
- data: {
683
- status: normalized?.statusCode ?? normalized?.status ?? null,
684
- code: normalized?.code ?? null,
685
- error: typeof normalized?.message === 'string' ? normalized.message : String(normalized || '')
686
- },
687
- headers: finalHeaders,
688
- url: targetUrl,
689
- entryEndpoint,
690
- clientRequestId
691
- });
692
- }
693
- catch { /* non-blocking */ }
694
- throw normalized;
695
- }
696
- // Provider 不处理工具修复/注入逻辑:统一收敛到 llmswitch-core 与兼容层
697
- // 此处不做任何自动修复/重试,保持单次请求的幂等与可观测性
698
- try { /* no-op */ }
699
- catch { /* ignore */ }
700
- return response;
392
+ return this.requestExecutor.execute(request, context);
701
393
  }
702
394
  wantsUpstreamSse(_request, _context) {
703
395
  return false;
@@ -733,6 +425,135 @@ export class HttpTransportProvider extends BaseProvider {
733
425
  return false;
734
426
  }
735
427
  }
428
+ getHttpRetryLimit() {
429
+ return 3;
430
+ }
431
+ async delayBeforeHttpRetry(attempt) {
432
+ const delay = Math.min(500 * attempt, 2000);
433
+ await new Promise((resolve) => setTimeout(resolve, delay));
434
+ }
435
+ shouldRetryHttpError(error, attempt, maxAttempts) {
436
+ if (attempt >= maxAttempts) {
437
+ return false;
438
+ }
439
+ const normalized = error;
440
+ const statusCode = extractStatusCodeFromError(normalized);
441
+ if (statusCode && statusCode >= 500) {
442
+ return true;
443
+ }
444
+ return false;
445
+ }
446
+ async tryRecoverOAuthAndReplay(error, requestInfo, processedRequest, captureSse, context) {
447
+ try {
448
+ const providerAuth = this.config.config.auth;
449
+ if (this.normalizeAuthMode(providerAuth.type) !== 'oauth') {
450
+ return undefined;
451
+ }
452
+ const shouldRetry = await handleUpstreamInvalidOAuthToken(this.oauthProviderId || this.providerType, providerAuth, error);
453
+ if (!shouldRetry) {
454
+ return undefined;
455
+ }
456
+ const retryHeaders = await this.buildRequestHeaders();
457
+ let finalRetryHeaders = await this.finalizeRequestHeaders(retryHeaders, processedRequest);
458
+ finalRetryHeaders = this.applyStreamModeHeaders(finalRetryHeaders, requestInfo.wantsSse);
459
+ if (requestInfo.wantsSse) {
460
+ const upstreamStream = await this.httpClient.postStream(requestInfo.endpoint, requestInfo.body, finalRetryHeaders);
461
+ const streamForHost = captureSse
462
+ ? attachProviderSseSnapshotStream(upstreamStream, {
463
+ requestId: context.requestId,
464
+ headers: finalRetryHeaders,
465
+ url: requestInfo.targetUrl,
466
+ entryEndpoint: requestInfo.entryEndpoint,
467
+ clientRequestId: requestInfo.clientRequestId,
468
+ extra: { retry: true }
469
+ })
470
+ : upstreamStream;
471
+ const wrapped = await this.wrapUpstreamSseResponse(streamForHost, context);
472
+ if (!captureSse) {
473
+ try {
474
+ await writeProviderSnapshot({
475
+ phase: 'provider-response',
476
+ requestId: context.requestId,
477
+ data: { mode: 'sse', retry: true },
478
+ headers: finalRetryHeaders,
479
+ url: requestInfo.targetUrl,
480
+ entryEndpoint: requestInfo.entryEndpoint,
481
+ clientRequestId: requestInfo.clientRequestId
482
+ });
483
+ }
484
+ catch { /* non-blocking */ }
485
+ }
486
+ return wrapped;
487
+ }
488
+ const response = await this.httpClient.post(requestInfo.endpoint, requestInfo.body, finalRetryHeaders);
489
+ try {
490
+ await writeProviderSnapshot({
491
+ phase: 'provider-response',
492
+ requestId: context.requestId,
493
+ data: response,
494
+ headers: finalRetryHeaders,
495
+ url: requestInfo.targetUrl,
496
+ entryEndpoint: requestInfo.entryEndpoint,
497
+ clientRequestId: requestInfo.clientRequestId
498
+ });
499
+ }
500
+ catch { /* non-blocking */ }
501
+ return response;
502
+ }
503
+ catch {
504
+ return undefined;
505
+ }
506
+ }
507
+ async normalizeHttpError(error, processedRequest, requestInfo, context) {
508
+ const normalized = error;
509
+ try {
510
+ const statusCode = extractStatusCodeFromError(normalized);
511
+ if (statusCode && !Number.isNaN(statusCode)) {
512
+ normalized.statusCode = statusCode;
513
+ if (!normalized.status) {
514
+ normalized.status = statusCode;
515
+ }
516
+ if (!normalized.code) {
517
+ normalized.code = `HTTP_${statusCode}`;
518
+ }
519
+ }
520
+ if (!normalized.response) {
521
+ normalized.response = {};
522
+ }
523
+ if (!normalized.response.data) {
524
+ normalized.response.data = {};
525
+ }
526
+ if (!normalized.response.data.error) {
527
+ normalized.response.data.error = {};
528
+ }
529
+ if (normalized.code && !normalized.response.data.error.code) {
530
+ normalized.response.data.error.code = normalized.code;
531
+ }
532
+ if (normalized.message && !normalized.response.data.error.message) {
533
+ normalized.response.data.error.message = normalized.message;
534
+ }
535
+ }
536
+ catch {
537
+ /* ignore */
538
+ }
539
+ try {
540
+ await writeProviderSnapshot({
541
+ phase: 'provider-error',
542
+ requestId: context.requestId,
543
+ data: {
544
+ status: normalized?.statusCode ?? normalized?.status ?? null,
545
+ code: normalized?.code ?? null,
546
+ error: typeof normalized?.message === 'string' ? normalized.message : String(error || '')
547
+ },
548
+ headers: requestInfo.headers,
549
+ url: requestInfo.targetUrl,
550
+ entryEndpoint: requestInfo.entryEndpoint ?? this.getEntryEndpointFromPayload(processedRequest),
551
+ clientRequestId: requestInfo.clientRequestId ?? this.getClientRequestIdFromContext(context)
552
+ });
553
+ }
554
+ catch { /* non-blocking */ }
555
+ return normalized;
556
+ }
736
557
  /**
737
558
  * 为特定请求确定最终 endpoint(默认使用配置值,可由子类覆写)
738
559
  */
@@ -774,6 +595,14 @@ export class HttpTransportProvider extends BaseProvider {
774
595
  const baseHeaders = {
775
596
  'Content-Type': 'application/json'
776
597
  };
598
+ const runtimeMetadata = this.getCurrentRuntimeMetadata();
599
+ const inboundMetadata = runtimeMetadata?.metadata;
600
+ const inboundUserAgent = typeof inboundMetadata?.userAgent === 'string' && inboundMetadata.userAgent.trim()
601
+ ? inboundMetadata.userAgent.trim()
602
+ : undefined;
603
+ const inboundOriginator = typeof inboundMetadata?.clientOriginator === 'string' && inboundMetadata.clientOriginator.trim()
604
+ ? inboundMetadata.clientOriginator.trim()
605
+ : undefined;
777
606
  // 服务特定头部
778
607
  const serviceHeaders = this.serviceProfile.headers || {};
779
608
  // 配置覆盖头部
@@ -785,14 +614,14 @@ export class HttpTransportProvider extends BaseProvider {
785
614
  if (this.normalizeAuthMode(auth.type) === 'oauth') {
786
615
  const oauthAuth = auth;
787
616
  const oauthProviderId = this.ensureOAuthProviderId(oauthAuth);
788
- console.log('[OAuth] [headers] ensureValid start (openBrowser=true, forceReauth=false)');
617
+ logOAuthDebug('[OAuth] [headers] ensureValid start (openBrowser=true, forceReauth=false)');
789
618
  try {
790
619
  await ensureValidOAuthToken(oauthProviderId, oauthAuth, {
791
620
  forceReacquireIfRefreshFails: true,
792
621
  openBrowser: true,
793
622
  forceReauthorize: false
794
623
  });
795
- console.log('[OAuth] [headers] ensureValid OK');
624
+ logOAuthDebug('[OAuth] [headers] ensureValid OK');
796
625
  }
797
626
  catch (error) {
798
627
  const err = error;
@@ -833,13 +662,42 @@ export class HttpTransportProvider extends BaseProvider {
833
662
  if (!('Accept' in finalHeaders) && !('accept' in finalHeaders)) {
834
663
  finalHeaders['Accept'] = 'application/json';
835
664
  }
836
- // 获取Hook管理器(新的统一系统)
837
- const hookManager = this.getHookManager();
838
- // 🔍 Hook 6: 认证阶段
839
- await hookManager.executeHookChain('authentication', 'auth', authHeaders, this.createProviderContext());
840
- // 🔍 Hook 7: Headers处理阶段
841
- const headersResult = await hookManager.executeHookChain('request_preprocessing', 'headers', finalHeaders, this.createProviderContext());
842
- finalHeaders = headersResult.data;
665
+ const getHeader = (headers, target) => {
666
+ const lowered = target.toLowerCase();
667
+ for (const [key, value] of Object.entries(headers)) {
668
+ if (key.toLowerCase() === lowered && typeof value === 'string' && value.trim()) {
669
+ return value.trim();
670
+ }
671
+ }
672
+ return undefined;
673
+ };
674
+ const setHeader = (headers, target, value) => {
675
+ if (!value || !value.trim())
676
+ return;
677
+ const lowered = target.toLowerCase();
678
+ for (const key of Object.keys(headers)) {
679
+ if (key.toLowerCase() === lowered) {
680
+ headers[key] = value;
681
+ return;
682
+ }
683
+ }
684
+ headers[target] = value;
685
+ };
686
+ // Header priority:
687
+ // - user/provider config (overrides/runtime) wins
688
+ // - otherwise inherit from inbound client headers
689
+ // - otherwise fall back to defaults
690
+ const uaFromConfig = getHeader({ ...overrideHeaders, ...runtimeHeaders }, 'User-Agent');
691
+ const uaFromService = getHeader(serviceHeaders, 'User-Agent');
692
+ const resolvedUa = uaFromConfig ?? inboundUserAgent ?? uaFromService ?? DEFAULT_USER_AGENT;
693
+ setHeader(finalHeaders, 'User-Agent', resolvedUa);
694
+ // originator: do not invent one; only forward from config or inbound client
695
+ const originatorFromConfig = getHeader({ ...overrideHeaders, ...runtimeHeaders }, 'originator');
696
+ const originatorFromService = getHeader(serviceHeaders, 'originator');
697
+ const resolvedOriginator = originatorFromConfig ?? inboundOriginator ?? originatorFromService;
698
+ if (resolvedOriginator) {
699
+ setHeader(finalHeaders, 'originator', resolvedOriginator);
700
+ }
843
701
  return finalHeaders;
844
702
  }
845
703
  getEffectiveBaseUrl() {
@@ -879,9 +737,6 @@ export class HttpTransportProvider extends BaseProvider {
879
737
  const trimmed = value.trim();
880
738
  return /^https?:\/\//i.test(trimmed) || trimmed.startsWith('//');
881
739
  }
882
- getHookManager() {
883
- return this.hookSystemIntegration.getBidirectionalHookManager();
884
- }
885
740
  // (工具自动修复辅助函数已删除)
886
741
  getConfigExtensions() {
887
742
  const extensions = this.config.config.extensions;