blackriver-gateway 3.8.46

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 (2415) hide show
  1. package/.env.example +2070 -0
  2. package/@omniroute/opencode-plugin/LICENSE +21 -0
  3. package/@omniroute/opencode-plugin/README.md +296 -0
  4. package/@omniroute/opencode-plugin/package-lock.json +1930 -0
  5. package/@omniroute/opencode-plugin/package.json +73 -0
  6. package/@omniroute/opencode-plugin/src/index.ts +4685 -0
  7. package/@omniroute/opencode-plugin/src/logger.ts +74 -0
  8. package/@omniroute/opencode-plugin/src/naming.ts +301 -0
  9. package/@omniroute/opencode-plugin/tsconfig.json +20 -0
  10. package/@omniroute/opencode-plugin/tsup.config.ts +20 -0
  11. package/@omniroute/opencode-provider/LICENSE +21 -0
  12. package/@omniroute/opencode-provider/README.md +156 -0
  13. package/@omniroute/opencode-provider/package-lock.json +1514 -0
  14. package/@omniroute/opencode-provider/package.json +63 -0
  15. package/@omniroute/opencode-provider/src/index.ts +908 -0
  16. package/@omniroute/opencode-provider/tsconfig.json +20 -0
  17. package/@omniroute/opencode-provider/tsup.config.ts +18 -0
  18. package/LICENSE +22 -0
  19. package/README.md +1265 -0
  20. package/bin/_ops-common.sh +70 -0
  21. package/bin/cli/CONVENTIONS.md +224 -0
  22. package/bin/cli/README.md +146 -0
  23. package/bin/cli/api-commands/agent-skills.mjs +70 -0
  24. package/bin/cli/api-commands/agentbridge.mjs +155 -0
  25. package/bin/cli/api-commands/api-keys.mjs +42 -0
  26. package/bin/cli/api-commands/audio.mjs +40 -0
  27. package/bin/cli/api-commands/chat.mjs +58 -0
  28. package/bin/cli/api-commands/cli-tools.mjs +361 -0
  29. package/bin/cli/api-commands/cloud.mjs +81 -0
  30. package/bin/cli/api-commands/combos.mjs +69 -0
  31. package/bin/cli/api-commands/compression.mjs +128 -0
  32. package/bin/cli/api-commands/embedded-services.mjs +202 -0
  33. package/bin/cli/api-commands/embeddings.mjs +42 -0
  34. package/bin/cli/api-commands/fallback.mjs +49 -0
  35. package/bin/cli/api-commands/images.mjs +42 -0
  36. package/bin/cli/api-commands/memory.mjs +251 -0
  37. package/bin/cli/api-commands/messages.mjs +40 -0
  38. package/bin/cli/api-commands/models.mjs +89 -0
  39. package/bin/cli/api-commands/moderations.mjs +24 -0
  40. package/bin/cli/api-commands/oauth.mjs +114 -0
  41. package/bin/cli/api-commands/playground.mjs +83 -0
  42. package/bin/cli/api-commands/pricing.mjs +44 -0
  43. package/bin/cli/api-commands/provider-nodes.mjs +62 -0
  44. package/bin/cli/api-commands/providers.mjs +148 -0
  45. package/bin/cli/api-commands/quota.mjs +154 -0
  46. package/bin/cli/api-commands/registry.mjs +75 -0
  47. package/bin/cli/api-commands/rerank.mjs +24 -0
  48. package/bin/cli/api-commands/responses.mjs +24 -0
  49. package/bin/cli/api-commands/settings.mjs +228 -0
  50. package/bin/cli/api-commands/system.mjs +320 -0
  51. package/bin/cli/api-commands/telemetry.mjs +26 -0
  52. package/bin/cli/api-commands/traffic-inspector.mjs +307 -0
  53. package/bin/cli/api-commands/translator.mjs +65 -0
  54. package/bin/cli/api-commands/usage.mjs +117 -0
  55. package/bin/cli/api.mjs +269 -0
  56. package/bin/cli/commands/a2a.mjs +323 -0
  57. package/bin/cli/commands/audit.mjs +203 -0
  58. package/bin/cli/commands/autostart.mjs +57 -0
  59. package/bin/cli/commands/backup.mjs +469 -0
  60. package/bin/cli/commands/batches.mjs +235 -0
  61. package/bin/cli/commands/cache.mjs +102 -0
  62. package/bin/cli/commands/chat.mjs +162 -0
  63. package/bin/cli/commands/cloud.mjs +233 -0
  64. package/bin/cli/commands/combo.mjs +361 -0
  65. package/bin/cli/commands/completion.mjs +346 -0
  66. package/bin/cli/commands/compression.mjs +247 -0
  67. package/bin/cli/commands/config.mjs +351 -0
  68. package/bin/cli/commands/configure.mjs +180 -0
  69. package/bin/cli/commands/connect.mjs +132 -0
  70. package/bin/cli/commands/context-eng.mjs +182 -0
  71. package/bin/cli/commands/contexts.mjs +271 -0
  72. package/bin/cli/commands/cost.mjs +144 -0
  73. package/bin/cli/commands/dashboard.mjs +65 -0
  74. package/bin/cli/commands/doctor.mjs +527 -0
  75. package/bin/cli/commands/env.mjs +100 -0
  76. package/bin/cli/commands/eval.mjs +278 -0
  77. package/bin/cli/commands/files.mjs +144 -0
  78. package/bin/cli/commands/health.mjs +123 -0
  79. package/bin/cli/commands/keys.mjs +599 -0
  80. package/bin/cli/commands/launch-codex.mjs +201 -0
  81. package/bin/cli/commands/launch.mjs +155 -0
  82. package/bin/cli/commands/login.mjs +192 -0
  83. package/bin/cli/commands/logs.mjs +183 -0
  84. package/bin/cli/commands/mcp.mjs +273 -0
  85. package/bin/cli/commands/memory.mjs +244 -0
  86. package/bin/cli/commands/models.mjs +92 -0
  87. package/bin/cli/commands/nodes.mjs +177 -0
  88. package/bin/cli/commands/oauth.mjs +258 -0
  89. package/bin/cli/commands/oneproxy.mjs +120 -0
  90. package/bin/cli/commands/open.mjs +75 -0
  91. package/bin/cli/commands/openapi.mjs +168 -0
  92. package/bin/cli/commands/plugin.mjs +219 -0
  93. package/bin/cli/commands/policy.mjs +216 -0
  94. package/bin/cli/commands/pricing.mjs +123 -0
  95. package/bin/cli/commands/provider-cmd.mjs +18 -0
  96. package/bin/cli/commands/providers.mjs +706 -0
  97. package/bin/cli/commands/quota.mjs +100 -0
  98. package/bin/cli/commands/redis.mjs +294 -0
  99. package/bin/cli/commands/registry.mjs +162 -0
  100. package/bin/cli/commands/repl.mjs +19 -0
  101. package/bin/cli/commands/reset-encrypted-columns.mjs +88 -0
  102. package/bin/cli/commands/resilience.mjs +208 -0
  103. package/bin/cli/commands/restart.mjs +25 -0
  104. package/bin/cli/commands/runtime.mjs +98 -0
  105. package/bin/cli/commands/serve.mjs +426 -0
  106. package/bin/cli/commands/sessions.mjs +108 -0
  107. package/bin/cli/commands/setup-aider.mjs +146 -0
  108. package/bin/cli/commands/setup-claude.mjs +219 -0
  109. package/bin/cli/commands/setup-cline.mjs +160 -0
  110. package/bin/cli/commands/setup-codex.mjs +387 -0
  111. package/bin/cli/commands/setup-continue.mjs +173 -0
  112. package/bin/cli/commands/setup-crush.mjs +148 -0
  113. package/bin/cli/commands/setup-cursor.mjs +108 -0
  114. package/bin/cli/commands/setup-goose.mjs +150 -0
  115. package/bin/cli/commands/setup-kilo.mjs +178 -0
  116. package/bin/cli/commands/setup-open-code.mjs +398 -0
  117. package/bin/cli/commands/setup-opencode.mjs +129 -0
  118. package/bin/cli/commands/setup-qwen.mjs +156 -0
  119. package/bin/cli/commands/setup-roo.mjs +172 -0
  120. package/bin/cli/commands/setup.mjs +211 -0
  121. package/bin/cli/commands/simulate.mjs +125 -0
  122. package/bin/cli/commands/skills.mjs +373 -0
  123. package/bin/cli/commands/status.mjs +95 -0
  124. package/bin/cli/commands/stop.mjs +96 -0
  125. package/bin/cli/commands/stream.mjs +166 -0
  126. package/bin/cli/commands/sync.mjs +230 -0
  127. package/bin/cli/commands/tags.mjs +116 -0
  128. package/bin/cli/commands/telemetry.mjs +74 -0
  129. package/bin/cli/commands/test-provider.mjs +242 -0
  130. package/bin/cli/commands/tokens.mjs +118 -0
  131. package/bin/cli/commands/translator.mjs +131 -0
  132. package/bin/cli/commands/tray.mjs +36 -0
  133. package/bin/cli/commands/tunnel.mjs +347 -0
  134. package/bin/cli/commands/update.mjs +194 -0
  135. package/bin/cli/commands/usage.mjs +331 -0
  136. package/bin/cli/commands/webhooks.mjs +196 -0
  137. package/bin/cli/contexts.mjs +60 -0
  138. package/bin/cli/data-dir.mjs +59 -0
  139. package/bin/cli/encryption.mjs +67 -0
  140. package/bin/cli/i18n.mjs +106 -0
  141. package/bin/cli/io.mjs +83 -0
  142. package/bin/cli/locales/ar.json +32 -0
  143. package/bin/cli/locales/az.json +32 -0
  144. package/bin/cli/locales/bg.json +32 -0
  145. package/bin/cli/locales/bn.json +5 -0
  146. package/bin/cli/locales/cs.json +32 -0
  147. package/bin/cli/locales/da.json +32 -0
  148. package/bin/cli/locales/de.json +32 -0
  149. package/bin/cli/locales/en.json +1289 -0
  150. package/bin/cli/locales/es.json +32 -0
  151. package/bin/cli/locales/fa.json +32 -0
  152. package/bin/cli/locales/fi.json +32 -0
  153. package/bin/cli/locales/fr.json +32 -0
  154. package/bin/cli/locales/gu.json +5 -0
  155. package/bin/cli/locales/he.json +5 -0
  156. package/bin/cli/locales/hi.json +32 -0
  157. package/bin/cli/locales/hu.json +32 -0
  158. package/bin/cli/locales/id.json +32 -0
  159. package/bin/cli/locales/in.json +5 -0
  160. package/bin/cli/locales/it.json +32 -0
  161. package/bin/cli/locales/ja.json +32 -0
  162. package/bin/cli/locales/ko.json +32 -0
  163. package/bin/cli/locales/mr.json +5 -0
  164. package/bin/cli/locales/ms.json +5 -0
  165. package/bin/cli/locales/nl.json +32 -0
  166. package/bin/cli/locales/no.json +32 -0
  167. package/bin/cli/locales/phi.json +5 -0
  168. package/bin/cli/locales/pl.json +32 -0
  169. package/bin/cli/locales/pt-BR.json +1286 -0
  170. package/bin/cli/locales/pt.json +32 -0
  171. package/bin/cli/locales/ro.json +32 -0
  172. package/bin/cli/locales/ru.json +32 -0
  173. package/bin/cli/locales/sk.json +32 -0
  174. package/bin/cli/locales/sv.json +32 -0
  175. package/bin/cli/locales/sw.json +5 -0
  176. package/bin/cli/locales/ta.json +5 -0
  177. package/bin/cli/locales/te.json +5 -0
  178. package/bin/cli/locales/th.json +32 -0
  179. package/bin/cli/locales/tr.json +32 -0
  180. package/bin/cli/locales/uk-UA.json +32 -0
  181. package/bin/cli/locales/ur.json +5 -0
  182. package/bin/cli/locales/vi.json +32 -0
  183. package/bin/cli/locales/zh-CN.json +1262 -0
  184. package/bin/cli/output.mjs +200 -0
  185. package/bin/cli/plugins.mjs +90 -0
  186. package/bin/cli/program.mjs +40 -0
  187. package/bin/cli/provider-catalog.mjs +175 -0
  188. package/bin/cli/provider-store.mjs +312 -0
  189. package/bin/cli/provider-test.mjs +181 -0
  190. package/bin/cli/runtime/index.mjs +19 -0
  191. package/bin/cli/runtime/magicBytes.mjs +47 -0
  192. package/bin/cli/runtime/nativeDeps.mjs +137 -0
  193. package/bin/cli/runtime/processSupervisor.mjs +136 -0
  194. package/bin/cli/runtime/sqliteRuntime.mjs +129 -0
  195. package/bin/cli/runtime/supervisorPolicy.mjs +56 -0
  196. package/bin/cli/runtime/trayRuntime.ts +94 -0
  197. package/bin/cli/runtime.mjs +61 -0
  198. package/bin/cli/schemas/output-schemas.mjs +56 -0
  199. package/bin/cli/scripts/generate-locales.mjs +911 -0
  200. package/bin/cli/settings-store.mjs +45 -0
  201. package/bin/cli/spinner.mjs +30 -0
  202. package/bin/cli/sqlite.mjs +158 -0
  203. package/bin/cli/tray/autostart.mjs +410 -0
  204. package/bin/cli/tray/autostart.ts +18 -0
  205. package/bin/cli/tray/icon.png +0 -0
  206. package/bin/cli/tray/index.mjs +28 -0
  207. package/bin/cli/tray/tray.ps1 +99 -0
  208. package/bin/cli/tray/tray.ts +186 -0
  209. package/bin/cli/tray/traySystray.mjs +135 -0
  210. package/bin/cli/tray/trayWin.ts +91 -0
  211. package/bin/cli/tray/trayWindows.mjs +75 -0
  212. package/bin/cli/tui/Dashboard.jsx +70 -0
  213. package/bin/cli/tui/EvalWatch.jsx +171 -0
  214. package/bin/cli/tui/InterfaceMenu.jsx +55 -0
  215. package/bin/cli/tui/OAuthFlow.jsx +193 -0
  216. package/bin/cli/tui/ProvidersTestAll.jsx +190 -0
  217. package/bin/cli/tui/Repl.jsx +392 -0
  218. package/bin/cli/tui/session.mjs +54 -0
  219. package/bin/cli/tui/tabs/Combos.jsx +47 -0
  220. package/bin/cli/tui/tabs/Cost.jsx +52 -0
  221. package/bin/cli/tui/tabs/Health.jsx +53 -0
  222. package/bin/cli/tui/tabs/Keys.jsx +48 -0
  223. package/bin/cli/tui/tabs/Logs.jsx +60 -0
  224. package/bin/cli/tui/tabs/Overview.jsx +80 -0
  225. package/bin/cli/tui/tabs/Providers.jsx +48 -0
  226. package/bin/cli/tui-components/CodeBlock.jsx +15 -0
  227. package/bin/cli/tui-components/ConfirmDialog.jsx +40 -0
  228. package/bin/cli/tui-components/DataTable.jsx +50 -0
  229. package/bin/cli/tui-components/HeaderSwr.jsx +25 -0
  230. package/bin/cli/tui-components/KeyMaskedDisplay.jsx +12 -0
  231. package/bin/cli/tui-components/MarkdownView.jsx +13 -0
  232. package/bin/cli/tui-components/MenuSelect.jsx +38 -0
  233. package/bin/cli/tui-components/MultilineInput.jsx +18 -0
  234. package/bin/cli/tui-components/ProgressBar.jsx +15 -0
  235. package/bin/cli/tui-components/Sparkline.jsx +15 -0
  236. package/bin/cli/tui-components/StatusBadge.jsx +17 -0
  237. package/bin/cli/tui-components/TokenCounter.jsx +18 -0
  238. package/bin/cli/tui-components/theme.jsx +11 -0
  239. package/bin/cli/utils/cliToken.mjs +22 -0
  240. package/bin/cli/utils/clipboard.mjs +35 -0
  241. package/bin/cli/utils/environment.mjs +50 -0
  242. package/bin/cli/utils/pid.mjs +110 -0
  243. package/bin/cli/utils/storageKeyProvision.mjs +31 -0
  244. package/bin/cold-start-bench.sh +82 -0
  245. package/bin/mcp-server.mjs +71 -0
  246. package/bin/nodeRuntimeSupport.mjs +84 -0
  247. package/bin/omniroute.mjs +237 -0
  248. package/bin/reset-password.mjs +137 -0
  249. package/bin/restore-data.sh +64 -0
  250. package/bin/restore-policies.sh +77 -0
  251. package/bin/rollback.sh +102 -0
  252. package/bin/snapshot-data.sh +61 -0
  253. package/open-sse/config/agyModels.ts +157 -0
  254. package/open-sse/config/anthropicHeaders.ts +127 -0
  255. package/open-sse/config/antigravityModelAliases.ts +309 -0
  256. package/open-sse/config/antigravityUpstream.ts +20 -0
  257. package/open-sse/config/audioRegistry.ts +547 -0
  258. package/open-sse/config/azureAi.ts +49 -0
  259. package/open-sse/config/bedrock.ts +196 -0
  260. package/open-sse/config/cliFingerprints.ts +405 -0
  261. package/open-sse/config/codexClient.ts +49 -0
  262. package/open-sse/config/codexIdentity.ts +87 -0
  263. package/open-sse/config/codexInstructions.ts +122 -0
  264. package/open-sse/config/codexQuotaScopes.ts +87 -0
  265. package/open-sse/config/constants.ts +281 -0
  266. package/open-sse/config/contextEditing.ts +156 -0
  267. package/open-sse/config/credentialLoader.ts +123 -0
  268. package/open-sse/config/datarobot.ts +69 -0
  269. package/open-sse/config/defaultThinkingSignature.ts +8 -0
  270. package/open-sse/config/embeddingRegistry.ts +434 -0
  271. package/open-sse/config/errorConfig.ts +201 -0
  272. package/open-sse/config/freeModelCatalog.data.ts +467 -0
  273. package/open-sse/config/freeModelCatalog.ts +142 -0
  274. package/open-sse/config/freeTierCatalog.ts +101 -0
  275. package/open-sse/config/geminiRateLimits.json +34 -0
  276. package/open-sse/config/glmProvider.ts +488 -0
  277. package/open-sse/config/imageRegistry.ts +752 -0
  278. package/open-sse/config/maritalk.ts +18 -0
  279. package/open-sse/config/mediaServiceKinds.ts +70 -0
  280. package/open-sse/config/moderationRegistry.ts +89 -0
  281. package/open-sse/config/musicRegistry.ts +115 -0
  282. package/open-sse/config/oci.ts +44 -0
  283. package/open-sse/config/ocrRegistry.ts +82 -0
  284. package/open-sse/config/ollamaModels.ts +28 -0
  285. package/open-sse/config/providerErrorRules.ts +218 -0
  286. package/open-sse/config/providerFieldStrips.ts +42 -0
  287. package/open-sse/config/providerHeaderProfiles.ts +188 -0
  288. package/open-sse/config/providerModels.ts +199 -0
  289. package/open-sse/config/providerPluginManifest.ts +186 -0
  290. package/open-sse/config/providerPluginManifestRegistry.ts +31 -0
  291. package/open-sse/config/providerPluginManifestUrl.ts +26 -0
  292. package/open-sse/config/providerRegistry.ts +257 -0
  293. package/open-sse/config/providers/index.ts +377 -0
  294. package/open-sse/config/providers/registry/adapta-web/index.ts +20 -0
  295. package/open-sse/config/providers/registry/agentrouter/index.ts +24 -0
  296. package/open-sse/config/providers/registry/agy/index.ts +28 -0
  297. package/open-sse/config/providers/registry/ai21/index.ts +13 -0
  298. package/open-sse/config/providers/registry/aimlapi/index.ts +21 -0
  299. package/open-sse/config/providers/registry/alibaba/cn/index.ts +15 -0
  300. package/open-sse/config/providers/registry/alibaba/index.ts +15 -0
  301. package/open-sse/config/providers/registry/anthropic/index.ts +52 -0
  302. package/open-sse/config/providers/registry/antigravity/index.ts +28 -0
  303. package/open-sse/config/providers/registry/api-airforce/index.ts +57 -0
  304. package/open-sse/config/providers/registry/auggie/index.ts +38 -0
  305. package/open-sse/config/providers/registry/bai/index.ts +14 -0
  306. package/open-sse/config/providers/registry/baichuan/index.ts +20 -0
  307. package/open-sse/config/providers/registry/baidu/index.ts +31 -0
  308. package/open-sse/config/providers/registry/bailian-coding-plan/index.ts +27 -0
  309. package/open-sse/config/providers/registry/baseten/index.ts +13 -0
  310. package/open-sse/config/providers/registry/bazaarlink/index.ts +48 -0
  311. package/open-sse/config/providers/registry/bedrock/index.ts +49 -0
  312. package/open-sse/config/providers/registry/blackbox/index.ts +25 -0
  313. package/open-sse/config/providers/registry/blackbox/web/index.ts +19 -0
  314. package/open-sse/config/providers/registry/bluesminds/index.ts +39 -0
  315. package/open-sse/config/providers/registry/byteplus/index.ts +21 -0
  316. package/open-sse/config/providers/registry/bytez/index.ts +17 -0
  317. package/open-sse/config/providers/registry/cerebras/index.ts +16 -0
  318. package/open-sse/config/providers/registry/charm-hyper/index.ts +14 -0
  319. package/open-sse/config/providers/registry/chatgpt-web/index.ts +23 -0
  320. package/open-sse/config/providers/registry/chipotle/index.ts +14 -0
  321. package/open-sse/config/providers/registry/chutes/index.ts +12 -0
  322. package/open-sse/config/providers/registry/claude/index.ts +102 -0
  323. package/open-sse/config/providers/registry/claude/web/index.ts +16 -0
  324. package/open-sse/config/providers/registry/cline/index.ts +45 -0
  325. package/open-sse/config/providers/registry/clinepass/index.ts +47 -0
  326. package/open-sse/config/providers/registry/cloudflare-ai/index.ts +33 -0
  327. package/open-sse/config/providers/registry/codebuddy-cn/index.ts +151 -0
  328. package/open-sse/config/providers/registry/codestral/index.ts +13 -0
  329. package/open-sse/config/providers/registry/codex/index.ts +115 -0
  330. package/open-sse/config/providers/registry/cohere/index.ts +28 -0
  331. package/open-sse/config/providers/registry/command-code/index.ts +143 -0
  332. package/open-sse/config/providers/registry/copilot-m365-web/index.ts +12 -0
  333. package/open-sse/config/providers/registry/copilot-web/index.ts +16 -0
  334. package/open-sse/config/providers/registry/coze/index.ts +12 -0
  335. package/open-sse/config/providers/registry/crof/index.ts +38 -0
  336. package/open-sse/config/providers/registry/cursor/index.ts +111 -0
  337. package/open-sse/config/providers/registry/databricks/index.ts +13 -0
  338. package/open-sse/config/providers/registry/deepinfra/index.ts +13 -0
  339. package/open-sse/config/providers/registry/deepseek/index.ts +15 -0
  340. package/open-sse/config/providers/registry/deepseek/web/index.ts +35 -0
  341. package/open-sse/config/providers/registry/devin-cli/index.ts +68 -0
  342. package/open-sse/config/providers/registry/dgrid/index.ts +15 -0
  343. package/open-sse/config/providers/registry/dify/index.ts +12 -0
  344. package/open-sse/config/providers/registry/digitalocean/index.ts +11 -0
  345. package/open-sse/config/providers/registry/dit/index.ts +41 -0
  346. package/open-sse/config/providers/registry/doubao/index.ts +28 -0
  347. package/open-sse/config/providers/registry/doubao/web/index.ts +15 -0
  348. package/open-sse/config/providers/registry/duckduckgo-web/index.ts +19 -0
  349. package/open-sse/config/providers/registry/factory/index.ts +33 -0
  350. package/open-sse/config/providers/registry/featherless-ai/index.ts +13 -0
  351. package/open-sse/config/providers/registry/fireworks/index.ts +35 -0
  352. package/open-sse/config/providers/registry/freeaiapikey/index.ts +38 -0
  353. package/open-sse/config/providers/registry/freemodel-dev/index.ts +19 -0
  354. package/open-sse/config/providers/registry/friendliai/index.ts +16 -0
  355. package/open-sse/config/providers/registry/galadriel/index.ts +13 -0
  356. package/open-sse/config/providers/registry/gemini/index.ts +63 -0
  357. package/open-sse/config/providers/registry/gemini/web/index.ts +16 -0
  358. package/open-sse/config/providers/registry/gigachat/index.ts +13 -0
  359. package/open-sse/config/providers/registry/github/index.ts +161 -0
  360. package/open-sse/config/providers/registry/github/models/index.ts +38 -0
  361. package/open-sse/config/providers/registry/gitlab-duo/index.ts +29 -0
  362. package/open-sse/config/providers/registry/gitlawb/gmi/index.ts +19 -0
  363. package/open-sse/config/providers/registry/gitlawb/index.ts +18 -0
  364. package/open-sse/config/providers/registry/glhf/index.ts +12 -0
  365. package/open-sse/config/providers/registry/glm/cn/index.ts +17 -0
  366. package/open-sse/config/providers/registry/glm/index.ts +16 -0
  367. package/open-sse/config/providers/registry/glm/t/index.ts +16 -0
  368. package/open-sse/config/providers/registry/grok-cli/index.ts +32 -0
  369. package/open-sse/config/providers/registry/grok-web/index.ts +18 -0
  370. package/open-sse/config/providers/registry/groq/index.ts +25 -0
  371. package/open-sse/config/providers/registry/hackclub/index.ts +19 -0
  372. package/open-sse/config/providers/registry/haiper/index.ts +15 -0
  373. package/open-sse/config/providers/registry/hcnsec/index.ts +11 -0
  374. package/open-sse/config/providers/registry/heroku/index.ts +13 -0
  375. package/open-sse/config/providers/registry/huggingchat/index.ts +143 -0
  376. package/open-sse/config/providers/registry/huggingface/index.ts +20 -0
  377. package/open-sse/config/providers/registry/hyperbolic/index.ts +21 -0
  378. package/open-sse/config/providers/registry/ideogram/index.ts +15 -0
  379. package/open-sse/config/providers/registry/iflytek/index.ts +21 -0
  380. package/open-sse/config/providers/registry/inclusionai/index.ts +12 -0
  381. package/open-sse/config/providers/registry/inference-net/index.ts +13 -0
  382. package/open-sse/config/providers/registry/inner-ai/index.ts +43 -0
  383. package/open-sse/config/providers/registry/kenari/index.ts +14 -0
  384. package/open-sse/config/providers/registry/kie/index.ts +27 -0
  385. package/open-sse/config/providers/registry/kilo-gateway/index.ts +21 -0
  386. package/open-sse/config/providers/registry/kilocode/index.ts +43 -0
  387. package/open-sse/config/providers/registry/kimi/coding/index.ts +17 -0
  388. package/open-sse/config/providers/registry/kimi/coding-apikey/index.ts +9 -0
  389. package/open-sse/config/providers/registry/kimi/index.ts +17 -0
  390. package/open-sse/config/providers/registry/kimi/web/index.ts +27 -0
  391. package/open-sse/config/providers/registry/kiro/index.ts +50 -0
  392. package/open-sse/config/providers/registry/kluster/index.ts +12 -0
  393. package/open-sse/config/providers/registry/lambda-ai/index.ts +13 -0
  394. package/open-sse/config/providers/registry/leonardo/index.ts +15 -0
  395. package/open-sse/config/providers/registry/liquid/index.ts +12 -0
  396. package/open-sse/config/providers/registry/llamagate/index.ts +13 -0
  397. package/open-sse/config/providers/registry/llm7/index.ts +27 -0
  398. package/open-sse/config/providers/registry/longcat/index.ts +24 -0
  399. package/open-sse/config/providers/registry/maritalk/index.ts +13 -0
  400. package/open-sse/config/providers/registry/meta-llama/index.ts +13 -0
  401. package/open-sse/config/providers/registry/mimocode/index.ts +16 -0
  402. package/open-sse/config/providers/registry/minimax/cn/index.ts +24 -0
  403. package/open-sse/config/providers/registry/minimax/index.ts +24 -0
  404. package/open-sse/config/providers/registry/mistral/index.ts +18 -0
  405. package/open-sse/config/providers/registry/modal/index.ts +12 -0
  406. package/open-sse/config/providers/registry/modelscope/index.ts +24 -0
  407. package/open-sse/config/providers/registry/monsterapi/index.ts +17 -0
  408. package/open-sse/config/providers/registry/moonshot/index.ts +13 -0
  409. package/open-sse/config/providers/registry/morph/index.ts +13 -0
  410. package/open-sse/config/providers/registry/muse-spark-web/index.ts +24 -0
  411. package/open-sse/config/providers/registry/nanogpt/index.ts +13 -0
  412. package/open-sse/config/providers/registry/nebius/index.ts +9 -0
  413. package/open-sse/config/providers/registry/nlpcloud/index.ts +19 -0
  414. package/open-sse/config/providers/registry/nous-research/index.ts +15 -0
  415. package/open-sse/config/providers/registry/novita/index.ts +15 -0
  416. package/open-sse/config/providers/registry/nscale/index.ts +13 -0
  417. package/open-sse/config/providers/registry/nube/index.ts +17 -0
  418. package/open-sse/config/providers/registry/nvidia/index.ts +36 -0
  419. package/open-sse/config/providers/registry/ollama-cloud/index.ts +27 -0
  420. package/open-sse/config/providers/registry/openadapter/index.ts +25 -0
  421. package/open-sse/config/providers/registry/openai/index.ts +43 -0
  422. package/open-sse/config/providers/registry/opencode/go/index.ts +61 -0
  423. package/open-sse/config/providers/registry/opencode/index.ts +51 -0
  424. package/open-sse/config/providers/registry/opencode/zen/index.ts +98 -0
  425. package/open-sse/config/providers/registry/openrouter/index.ts +17 -0
  426. package/open-sse/config/providers/registry/orcarouter/index.ts +87 -0
  427. package/open-sse/config/providers/registry/ovhcloud/index.ts +13 -0
  428. package/open-sse/config/providers/registry/perplexity/index.ts +22 -0
  429. package/open-sse/config/providers/registry/perplexity/web/index.ts +23 -0
  430. package/open-sse/config/providers/registry/pioneer/index.ts +41 -0
  431. package/open-sse/config/providers/registry/pollinations/index.ts +51 -0
  432. package/open-sse/config/providers/registry/predibase/index.ts +13 -0
  433. package/open-sse/config/providers/registry/publicai/index.ts +13 -0
  434. package/open-sse/config/providers/registry/puter/index.ts +70 -0
  435. package/open-sse/config/providers/registry/qianfan/index.ts +18 -0
  436. package/open-sse/config/providers/registry/qiniu/index.ts +15 -0
  437. package/open-sse/config/providers/registry/qoder/index.ts +37 -0
  438. package/open-sse/config/providers/registry/qwen/index.ts +25 -0
  439. package/open-sse/config/providers/registry/qwen/web/index.ts +24 -0
  440. package/open-sse/config/providers/registry/reka/index.ts +18 -0
  441. package/open-sse/config/providers/registry/requesty/index.ts +11 -0
  442. package/open-sse/config/providers/registry/sambanova/index.ts +13 -0
  443. package/open-sse/config/providers/registry/scaleway/index.ts +20 -0
  444. package/open-sse/config/providers/registry/sensenova/index.ts +27 -0
  445. package/open-sse/config/providers/registry/siliconflow/index.ts +84 -0
  446. package/open-sse/config/providers/registry/snowflake/index.ts +13 -0
  447. package/open-sse/config/providers/registry/sparkdesk/index.ts +20 -0
  448. package/open-sse/config/providers/registry/stepfun/index.ts +20 -0
  449. package/open-sse/config/providers/registry/sumopod/index.ts +15 -0
  450. package/open-sse/config/providers/registry/suno/index.ts +18 -0
  451. package/open-sse/config/providers/registry/synthetic/index.ts +21 -0
  452. package/open-sse/config/providers/registry/t3-web/index.ts +45 -0
  453. package/open-sse/config/providers/registry/tencent/index.ts +25 -0
  454. package/open-sse/config/providers/registry/theoldllm/index.ts +51 -0
  455. package/open-sse/config/providers/registry/together/index.ts +20 -0
  456. package/open-sse/config/providers/registry/tokenrouter/index.ts +44 -0
  457. package/open-sse/config/providers/registry/trae/index.ts +24 -0
  458. package/open-sse/config/providers/registry/udio/index.ts +12 -0
  459. package/open-sse/config/providers/registry/uncloseai/index.ts +19 -0
  460. package/open-sse/config/providers/registry/upstage/index.ts +13 -0
  461. package/open-sse/config/providers/registry/v0-vercel/index.ts +13 -0
  462. package/open-sse/config/providers/registry/venice/index.ts +13 -0
  463. package/open-sse/config/providers/registry/veoaifree-web/index.ts +15 -0
  464. package/open-sse/config/providers/registry/vercel-ai-gateway/index.ts +13 -0
  465. package/open-sse/config/providers/registry/vertex/index.ts +33 -0
  466. package/open-sse/config/providers/registry/vertex/partner/index.ts +22 -0
  467. package/open-sse/config/providers/registry/volcengine/index.ts +13 -0
  468. package/open-sse/config/providers/registry/wafer/index.ts +19 -0
  469. package/open-sse/config/providers/registry/wandb/index.ts +13 -0
  470. package/open-sse/config/providers/registry/windsurf/index.ts +119 -0
  471. package/open-sse/config/providers/registry/x5lab/index.ts +15 -0
  472. package/open-sse/config/providers/registry/xai/index.ts +18 -0
  473. package/open-sse/config/providers/registry/xiaomi-mimo/index.ts +13 -0
  474. package/open-sse/config/providers/registry/yi/index.ts +12 -0
  475. package/open-sse/config/providers/registry/yuanbao-web/index.ts +37 -0
  476. package/open-sse/config/providers/registry/zai/index.ts +28 -0
  477. package/open-sse/config/providers/registry/zed-hosted/index.ts +35 -0
  478. package/open-sse/config/providers/registry/zenmux/index.ts +87 -0
  479. package/open-sse/config/providers/registry/zenmux-free/index.ts +40 -0
  480. package/open-sse/config/providers/shared.ts +766 -0
  481. package/open-sse/config/registryUtils.ts +134 -0
  482. package/open-sse/config/rerankRegistry.ts +196 -0
  483. package/open-sse/config/runway.ts +39 -0
  484. package/open-sse/config/sap.ts +58 -0
  485. package/open-sse/config/searchRegistry.ts +317 -0
  486. package/open-sse/config/toolCloaking.ts +253 -0
  487. package/open-sse/config/videoRegistry.ts +229 -0
  488. package/open-sse/config/watsonx.ts +43 -0
  489. package/open-sse/executors/adapta-web.ts +564 -0
  490. package/open-sse/executors/antigravity/sseCollect.ts +148 -0
  491. package/open-sse/executors/antigravity.ts +1716 -0
  492. package/open-sse/executors/antigravityUpstreamError.ts +25 -0
  493. package/open-sse/executors/auggie.ts +573 -0
  494. package/open-sse/executors/azure-openai.ts +48 -0
  495. package/open-sse/executors/base/headers.ts +93 -0
  496. package/open-sse/executors/base/reasoningEffort.ts +160 -0
  497. package/open-sse/executors/base.ts +1315 -0
  498. package/open-sse/executors/bedrock.ts +714 -0
  499. package/open-sse/executors/blackbox-web.ts +708 -0
  500. package/open-sse/executors/chatgpt-web/models.ts +133 -0
  501. package/open-sse/executors/chatgpt-web.ts +3107 -0
  502. package/open-sse/executors/chatgptWebErrors.ts +18 -0
  503. package/open-sse/executors/chatgptWebTools.ts +127 -0
  504. package/open-sse/executors/chipotle.ts +432 -0
  505. package/open-sse/executors/claude-web/payload.ts +230 -0
  506. package/open-sse/executors/claude-web-with-auto-refresh.ts +117 -0
  507. package/open-sse/executors/claude-web.ts +833 -0
  508. package/open-sse/executors/claudeIdentity.ts +445 -0
  509. package/open-sse/executors/cliproxyapi.ts +466 -0
  510. package/open-sse/executors/cloudflare-ai.ts +97 -0
  511. package/open-sse/executors/codebuddy-cn.ts +53 -0
  512. package/open-sse/executors/codex/quota.ts +114 -0
  513. package/open-sse/executors/codex/tools.ts +165 -0
  514. package/open-sse/executors/codex.ts +1270 -0
  515. package/open-sse/executors/commandCode.ts +716 -0
  516. package/open-sse/executors/copilot-m365-connection.ts +271 -0
  517. package/open-sse/executors/copilot-m365-frames.ts +279 -0
  518. package/open-sse/executors/copilot-m365-web.ts +341 -0
  519. package/open-sse/executors/copilot-web.ts +722 -0
  520. package/open-sse/executors/cursor/composer.ts +53 -0
  521. package/open-sse/executors/cursor/prompt.ts +75 -0
  522. package/open-sse/executors/cursor.ts +1465 -0
  523. package/open-sse/executors/deepseek-web/stream-format.ts +44 -0
  524. package/open-sse/executors/deepseek-web-with-auto-refresh.ts +184 -0
  525. package/open-sse/executors/deepseek-web.ts +1123 -0
  526. package/open-sse/executors/default/urlNormalizers.ts +64 -0
  527. package/open-sse/executors/default.ts +873 -0
  528. package/open-sse/executors/devin-cli.ts +470 -0
  529. package/open-sse/executors/doubao-web.ts +665 -0
  530. package/open-sse/executors/duckduckgo-web/challenge.ts +151 -0
  531. package/open-sse/executors/duckduckgo-web.ts +782 -0
  532. package/open-sse/executors/firecrawl-fetch.ts +177 -0
  533. package/open-sse/executors/forceResponsesUpstream.ts +60 -0
  534. package/open-sse/executors/gemini-business.ts +446 -0
  535. package/open-sse/executors/gemini-web.ts +413 -0
  536. package/open-sse/executors/github.ts +376 -0
  537. package/open-sse/executors/gitlab.ts +793 -0
  538. package/open-sse/executors/gitlabResponses.ts +191 -0
  539. package/open-sse/executors/glm.ts +520 -0
  540. package/open-sse/executors/grok-cli.ts +253 -0
  541. package/open-sse/executors/grok-web/native-tools.ts +182 -0
  542. package/open-sse/executors/grok-web/text-cleanup.ts +137 -0
  543. package/open-sse/executors/grok-web/tool-bridge.ts +666 -0
  544. package/open-sse/executors/grok-web/types.ts +47 -0
  545. package/open-sse/executors/grok-web.ts +883 -0
  546. package/open-sse/executors/huggingchat/jsonlStream.ts +221 -0
  547. package/open-sse/executors/huggingchat.ts +593 -0
  548. package/open-sse/executors/index.ts +232 -0
  549. package/open-sse/executors/inner-ai.ts +764 -0
  550. package/open-sse/executors/jina-reader-fetch.ts +119 -0
  551. package/open-sse/executors/kie.ts +109 -0
  552. package/open-sse/executors/kimi-web.ts +455 -0
  553. package/open-sse/executors/kimi.ts +133 -0
  554. package/open-sse/executors/kiro/eventstream.ts +192 -0
  555. package/open-sse/executors/kiro.ts +821 -0
  556. package/open-sse/executors/kiroThinking.ts +116 -0
  557. package/open-sse/executors/lmarena.ts +511 -0
  558. package/open-sse/executors/mimocode.ts +560 -0
  559. package/open-sse/executors/muse-spark-web/response-parser.ts +383 -0
  560. package/open-sse/executors/muse-spark-web.ts +928 -0
  561. package/open-sse/executors/ninerouter.ts +212 -0
  562. package/open-sse/executors/nlpcloud.ts +546 -0
  563. package/open-sse/executors/opencode.ts +336 -0
  564. package/open-sse/executors/perplexity-web/protocol.ts +503 -0
  565. package/open-sse/executors/perplexity-web.ts +545 -0
  566. package/open-sse/executors/poe-web.ts +158 -0
  567. package/open-sse/executors/pollinations.ts +119 -0
  568. package/open-sse/executors/puter.ts +59 -0
  569. package/open-sse/executors/qoder.ts +416 -0
  570. package/open-sse/executors/qwen-web.ts +474 -0
  571. package/open-sse/executors/t3-chat-web.ts +582 -0
  572. package/open-sse/executors/tavily-fetch.ts +103 -0
  573. package/open-sse/executors/theoldllm.ts +354 -0
  574. package/open-sse/executors/tinyfish-fetch.ts +131 -0
  575. package/open-sse/executors/trae.ts +481 -0
  576. package/open-sse/executors/v0-vercel-web.ts +164 -0
  577. package/open-sse/executors/venice-web.ts +165 -0
  578. package/open-sse/executors/veoaifree-web.ts +397 -0
  579. package/open-sse/executors/vertex.ts +208 -0
  580. package/open-sse/executors/vertexMedia.ts +341 -0
  581. package/open-sse/executors/windsurf.ts +706 -0
  582. package/open-sse/executors/xai.ts +94 -0
  583. package/open-sse/executors/yuanbao-web.ts +504 -0
  584. package/open-sse/executors/zed-hosted.ts +364 -0
  585. package/open-sse/executors/zenmux-free.ts +283 -0
  586. package/open-sse/handlers/audioSpeech.ts +1060 -0
  587. package/open-sse/handlers/audioTranscription.ts +554 -0
  588. package/open-sse/handlers/audioTranslation.ts +135 -0
  589. package/open-sse/handlers/chatCore/attemptLogging.ts +210 -0
  590. package/open-sse/handlers/chatCore/backgroundRedirect.ts +41 -0
  591. package/open-sse/handlers/chatCore/cacheUsageMeta.ts +65 -0
  592. package/open-sse/handlers/chatCore/cavemanOutputAnalytics.ts +47 -0
  593. package/open-sse/handlers/chatCore/claudeClassifierCompat.ts +99 -0
  594. package/open-sse/handlers/chatCore/claudeEffortVariant.ts +62 -0
  595. package/open-sse/handlers/chatCore/claudeMessageTypes.ts +15 -0
  596. package/open-sse/handlers/chatCore/claudeSystemRole.ts +48 -0
  597. package/open-sse/handlers/chatCore/claudeToolDefaults.ts +27 -0
  598. package/open-sse/handlers/chatCore/claudeUpstreamMessages.ts +143 -0
  599. package/open-sse/handlers/chatCore/clientUsageBuffer.ts +54 -0
  600. package/open-sse/handlers/chatCore/clineResponseEnvelope.ts +25 -0
  601. package/open-sse/handlers/chatCore/codexFailover.ts +41 -0
  602. package/open-sse/handlers/chatCore/codexQuota.ts +85 -0
  603. package/open-sse/handlers/chatCore/comboContextCache.ts +63 -0
  604. package/open-sse/handlers/chatCore/compressionAnalyticsWrite.ts +149 -0
  605. package/open-sse/handlers/chatCore/compressionCacheStats.ts +53 -0
  606. package/open-sse/handlers/chatCore/compressionComboPredicates.ts +41 -0
  607. package/open-sse/handlers/chatCore/compressionSettings.ts +35 -0
  608. package/open-sse/handlers/chatCore/compressionUsageReceipt.ts +27 -0
  609. package/open-sse/handlers/chatCore/contextEditingTelemetry.ts +40 -0
  610. package/open-sse/handlers/chatCore/cooldownClassification.ts +26 -0
  611. package/open-sse/handlers/chatCore/executionCredentials.ts +72 -0
  612. package/open-sse/handlers/chatCore/executorClientHeaders.ts +36 -0
  613. package/open-sse/handlers/chatCore/executorHelpers.ts +151 -0
  614. package/open-sse/handlers/chatCore/executorProxy.ts +117 -0
  615. package/open-sse/handlers/chatCore/failureUsage.ts +41 -0
  616. package/open-sse/handlers/chatCore/gamificationEvent.ts +28 -0
  617. package/open-sse/handlers/chatCore/headers.ts +65 -0
  618. package/open-sse/handlers/chatCore/idempotency.ts +65 -0
  619. package/open-sse/handlers/chatCore/jsonBodyToSse.ts +161 -0
  620. package/open-sse/handlers/chatCore/keyHealth.ts +113 -0
  621. package/open-sse/handlers/chatCore/logTruncation.ts +93 -0
  622. package/open-sse/handlers/chatCore/memoryExtraction.ts +131 -0
  623. package/open-sse/handlers/chatCore/memorySkillsInjection.ts +165 -0
  624. package/open-sse/handlers/chatCore/nonStreamingJsonResponse.ts +16 -0
  625. package/open-sse/handlers/chatCore/nonStreamingResponseBody.ts +155 -0
  626. package/open-sse/handlers/chatCore/nonStreamingResponseHeaders.ts +46 -0
  627. package/open-sse/handlers/chatCore/nonStreamingResponseParse.ts +135 -0
  628. package/open-sse/handlers/chatCore/nonStreamingSse.ts +171 -0
  629. package/open-sse/handlers/chatCore/nonStreamingUsageStats.ts +90 -0
  630. package/open-sse/handlers/chatCore/outputStyleTelemetry.ts +53 -0
  631. package/open-sse/handlers/chatCore/passthroughHelpers.ts +83 -0
  632. package/open-sse/handlers/chatCore/passthroughToolNames.ts +65 -0
  633. package/open-sse/handlers/chatCore/pluginOnRequest.ts +65 -0
  634. package/open-sse/handlers/chatCore/pluginOnResponse.ts +34 -0
  635. package/open-sse/handlers/chatCore/postCallGuardrailContext.ts +47 -0
  636. package/open-sse/handlers/chatCore/quotaShareConsumption.ts +41 -0
  637. package/open-sse/handlers/chatCore/requestFormat.ts +111 -0
  638. package/open-sse/handlers/chatCore/requestSetup.ts +51 -0
  639. package/open-sse/handlers/chatCore/responseHeaders.ts +138 -0
  640. package/open-sse/handlers/chatCore/sanitization.ts +63 -0
  641. package/open-sse/handlers/chatCore/semanticCache.ts +98 -0
  642. package/open-sse/handlers/chatCore/semanticCacheStore.ts +73 -0
  643. package/open-sse/handlers/chatCore/serviceTier.ts +70 -0
  644. package/open-sse/handlers/chatCore/skillsFormat.ts +33 -0
  645. package/open-sse/handlers/chatCore/stageTrace.ts +30 -0
  646. package/open-sse/handlers/chatCore/streamErrorResult.ts +58 -0
  647. package/open-sse/handlers/chatCore/streamFinalize.ts +48 -0
  648. package/open-sse/handlers/chatCore/streamingCost.ts +37 -0
  649. package/open-sse/handlers/chatCore/streamingPipeline.ts +99 -0
  650. package/open-sse/handlers/chatCore/streamingQuotaShare.ts +54 -0
  651. package/open-sse/handlers/chatCore/streamingResponseHeaders.ts +39 -0
  652. package/open-sse/handlers/chatCore/streamingSemanticCacheStore.ts +95 -0
  653. package/open-sse/handlers/chatCore/streamingUsageStats.ts +79 -0
  654. package/open-sse/handlers/chatCore/targetFormat.ts +34 -0
  655. package/open-sse/handlers/chatCore/telemetryHelpers.ts +78 -0
  656. package/open-sse/handlers/chatCore/upstreamBody.ts +168 -0
  657. package/open-sse/handlers/chatCore/upstreamExecuteHeaders.ts +71 -0
  658. package/open-sse/handlers/chatCore/upstreamTimeouts.ts +158 -0
  659. package/open-sse/handlers/chatCore.ts +4452 -0
  660. package/open-sse/handlers/embeddings.ts +378 -0
  661. package/open-sse/handlers/imageGeneration/providers/chatgptWeb.ts +187 -0
  662. package/open-sse/handlers/imageGeneration/providers/comfyUI.ts +116 -0
  663. package/open-sse/handlers/imageGeneration/providers/haiper.ts +120 -0
  664. package/open-sse/handlers/imageGeneration/providers/huggingface.ts +90 -0
  665. package/open-sse/handlers/imageGeneration/providers/hyperbolic.ts +100 -0
  666. package/open-sse/handlers/imageGeneration/providers/ideogram.ts +96 -0
  667. package/open-sse/handlers/imageGeneration/providers/imagen3.ts +136 -0
  668. package/open-sse/handlers/imageGeneration/providers/leonardo.ts +140 -0
  669. package/open-sse/handlers/imageGeneration/providers/nvidiaNim.ts +286 -0
  670. package/open-sse/handlers/imageGeneration/providers/sdWebUI.ts +94 -0
  671. package/open-sse/handlers/imageGeneration.ts +2880 -0
  672. package/open-sse/handlers/moderations.ts +82 -0
  673. package/open-sse/handlers/musicGeneration.ts +640 -0
  674. package/open-sse/handlers/ocr.ts +79 -0
  675. package/open-sse/handlers/rerank.ts +210 -0
  676. package/open-sse/handlers/responseSanitizer/reasoning.ts +149 -0
  677. package/open-sse/handlers/responseSanitizer.ts +1086 -0
  678. package/open-sse/handlers/responseTranslator.ts +640 -0
  679. package/open-sse/handlers/responsesHandler.ts +92 -0
  680. package/open-sse/handlers/search.ts +1545 -0
  681. package/open-sse/handlers/sseParser.ts +825 -0
  682. package/open-sse/handlers/usageExtractor.ts +93 -0
  683. package/open-sse/handlers/videoGeneration/googleFlow.ts +219 -0
  684. package/open-sse/handlers/videoGeneration/googleFlowHandler.ts +145 -0
  685. package/open-sse/handlers/videoGeneration.ts +1264 -0
  686. package/open-sse/handlers/webFetch.ts +130 -0
  687. package/open-sse/index.ts +170 -0
  688. package/open-sse/lib/deepseek-pow-solver.cjs +3 -0
  689. package/open-sse/lib/deepseek-pow.ts +189 -0
  690. package/open-sse/lib/sha3_wasm_bg.wasm +0 -0
  691. package/open-sse/mcp-server/README.md +611 -0
  692. package/open-sse/mcp-server/audit.ts +478 -0
  693. package/open-sse/mcp-server/catalog.ts +290 -0
  694. package/open-sse/mcp-server/descriptionCompressor.ts +243 -0
  695. package/open-sse/mcp-server/httpAuthContext.ts +42 -0
  696. package/open-sse/mcp-server/httpTransport.ts +306 -0
  697. package/open-sse/mcp-server/index.ts +19 -0
  698. package/open-sse/mcp-server/mcpCallerIdentity.ts +53 -0
  699. package/open-sse/mcp-server/runtimeHeartbeat.ts +162 -0
  700. package/open-sse/mcp-server/schemas/a2a.ts +203 -0
  701. package/open-sse/mcp-server/schemas/audit.ts +121 -0
  702. package/open-sse/mcp-server/schemas/index.ts +116 -0
  703. package/open-sse/mcp-server/schemas/pickFastestModel.ts +110 -0
  704. package/open-sse/mcp-server/schemas/toolDefinition.ts +31 -0
  705. package/open-sse/mcp-server/schemas/toolSearch.ts +37 -0
  706. package/open-sse/mcp-server/schemas/tools.ts +1481 -0
  707. package/open-sse/mcp-server/scopeEnforcement.ts +135 -0
  708. package/open-sse/mcp-server/server.ts +1370 -0
  709. package/open-sse/mcp-server/toolCardinality.ts +254 -0
  710. package/open-sse/mcp-server/toolSearch/catalog.ts +98 -0
  711. package/open-sse/mcp-server/toolSearch/handler.ts +18 -0
  712. package/open-sse/mcp-server/toolSearch/index.ts +5 -0
  713. package/open-sse/mcp-server/toolSearch/register.ts +36 -0
  714. package/open-sse/mcp-server/toolSearch/search.ts +96 -0
  715. package/open-sse/mcp-server/toolSearch/signature.ts +92 -0
  716. package/open-sse/mcp-server/tools/advancedTools.ts +1119 -0
  717. package/open-sse/mcp-server/tools/agentSkillTools.ts +83 -0
  718. package/open-sse/mcp-server/tools/compressionTools.ts +451 -0
  719. package/open-sse/mcp-server/tools/gamificationTools.ts +148 -0
  720. package/open-sse/mcp-server/tools/githubSkillTools.ts +141 -0
  721. package/open-sse/mcp-server/tools/memoryTools.ts +137 -0
  722. package/open-sse/mcp-server/tools/notionTools.ts +106 -0
  723. package/open-sse/mcp-server/tools/obsidianTools.ts +327 -0
  724. package/open-sse/mcp-server/tools/pickFastestModel.ts +293 -0
  725. package/open-sse/mcp-server/tools/pluginTools.ts +216 -0
  726. package/open-sse/mcp-server/tools/poolTools.ts +205 -0
  727. package/open-sse/mcp-server/tools/skillTools.ts +119 -0
  728. package/open-sse/package.json +17 -0
  729. package/open-sse/services/AGENTS.md +78 -0
  730. package/open-sse/services/accountFallback.ts +1863 -0
  731. package/open-sse/services/accountSelector.ts +80 -0
  732. package/open-sse/services/accountSemaphore.ts +367 -0
  733. package/open-sse/services/antigravity429Engine.ts +178 -0
  734. package/open-sse/services/antigravityClientProfile.ts +167 -0
  735. package/open-sse/services/antigravityCredits.ts +68 -0
  736. package/open-sse/services/antigravityHeaderScrub.ts +75 -0
  737. package/open-sse/services/antigravityHeaders.ts +121 -0
  738. package/open-sse/services/antigravityIdentity.ts +127 -0
  739. package/open-sse/services/antigravityObfuscation.ts +64 -0
  740. package/open-sse/services/antigravityProjectBootstrap.ts +145 -0
  741. package/open-sse/services/antigravityQuotaFamily.ts +56 -0
  742. package/open-sse/services/antigravityVersion.ts +163 -0
  743. package/open-sse/services/apiKeyRotator.ts +385 -0
  744. package/open-sse/services/autoCombo/autoPrefix.ts +61 -0
  745. package/open-sse/services/autoCombo/builtinCatalog.ts +126 -0
  746. package/open-sse/services/autoCombo/complexityRouter.ts +111 -0
  747. package/open-sse/services/autoCombo/engine.ts +314 -0
  748. package/open-sse/services/autoCombo/index.ts +18 -0
  749. package/open-sse/services/autoCombo/modePacks.ts +105 -0
  750. package/open-sse/services/autoCombo/modelFamily.ts +95 -0
  751. package/open-sse/services/autoCombo/paidModelFilter.ts +44 -0
  752. package/open-sse/services/autoCombo/persistence.ts +124 -0
  753. package/open-sse/services/autoCombo/pipelineRouter.ts +418 -0
  754. package/open-sse/services/autoCombo/providerDiversity.ts +170 -0
  755. package/open-sse/services/autoCombo/providerRegistryAccessor.ts +9 -0
  756. package/open-sse/services/autoCombo/requestControls.ts +80 -0
  757. package/open-sse/services/autoCombo/routerStrategy.ts +375 -0
  758. package/open-sse/services/autoCombo/scoring.ts +242 -0
  759. package/open-sse/services/autoCombo/selfHealing.ts +167 -0
  760. package/open-sse/services/autoCombo/speedRanking.ts +327 -0
  761. package/open-sse/services/autoCombo/suffixComposition.ts +147 -0
  762. package/open-sse/services/autoCombo/taskFitness.ts +407 -0
  763. package/open-sse/services/autoCombo/virtualFactory.ts +446 -0
  764. package/open-sse/services/autoRefreshDaemon.ts +206 -0
  765. package/open-sse/services/backgroundTaskDetector.ts +263 -0
  766. package/open-sse/services/bailianQuotaFetcher.ts +333 -0
  767. package/open-sse/services/batchProcessor.ts +914 -0
  768. package/open-sse/services/bedrock.ts +160 -0
  769. package/open-sse/services/browserBackedChat.ts +849 -0
  770. package/open-sse/services/browserPool.ts +501 -0
  771. package/open-sse/services/ccBridgeTransforms.ts +581 -0
  772. package/open-sse/services/ccWireImageBuiltins.ts +26 -0
  773. package/open-sse/services/chatgptImageCache.ts +143 -0
  774. package/open-sse/services/chatgptTlsClient.ts +628 -0
  775. package/open-sse/services/claudeAdaptiveThinking.ts +52 -0
  776. package/open-sse/services/claudeCodeCCH.ts +49 -0
  777. package/open-sse/services/claudeCodeCompatible.ts +1198 -0
  778. package/open-sse/services/claudeCodeCompatibleBeta.ts +23 -0
  779. package/open-sse/services/claudeCodeCompatibleThinkingDisplay.ts +34 -0
  780. package/open-sse/services/claudeCodeConstraints.ts +144 -0
  781. package/open-sse/services/claudeCodeExtraRemap.ts +18 -0
  782. package/open-sse/services/claudeCodeFingerprint.ts +46 -0
  783. package/open-sse/services/claudeCodeObfuscation.ts +116 -0
  784. package/open-sse/services/claudeCodeToolRemapper.ts +339 -0
  785. package/open-sse/services/claudeHaikuConstraints.ts +88 -0
  786. package/open-sse/services/claudeTlsClient.ts +590 -0
  787. package/open-sse/services/claudeTurnstileSolver.ts +206 -0
  788. package/open-sse/services/claudeUsageCooldown.ts +51 -0
  789. package/open-sse/services/claudeWebAutoRefresh.ts +224 -0
  790. package/open-sse/services/clinepassModels.ts +76 -0
  791. package/open-sse/services/cloudCodeHeaders.ts +41 -0
  792. package/open-sse/services/cloudCodeThinking.ts +71 -0
  793. package/open-sse/services/codeAssistSubscription.ts +83 -0
  794. package/open-sse/services/codexQuotaFetcher.ts +523 -0
  795. package/open-sse/services/codexUsageQuotas.ts +270 -0
  796. package/open-sse/services/codexVerbosity.ts +59 -0
  797. package/open-sse/services/combo/applyStrategyOrdering.ts +226 -0
  798. package/open-sse/services/combo/autoConfig.ts +62 -0
  799. package/open-sse/services/combo/autoStrategy.ts +475 -0
  800. package/open-sse/services/combo/comboCompatFallback.ts +82 -0
  801. package/open-sse/services/combo/comboCooldownRetry.ts +237 -0
  802. package/open-sse/services/combo/comboData.ts +24 -0
  803. package/open-sse/services/combo/comboPredicates.ts +211 -0
  804. package/open-sse/services/combo/comboSetup.ts +135 -0
  805. package/open-sse/services/combo/comboStructure.ts +766 -0
  806. package/open-sse/services/combo/concurrencyCaps.ts +76 -0
  807. package/open-sse/services/combo/context.ts +46 -0
  808. package/open-sse/services/combo/fingerprintExpansion.ts +105 -0
  809. package/open-sse/services/combo/headroomRanking.ts +91 -0
  810. package/open-sse/services/combo/providerWildcard.ts +253 -0
  811. package/open-sse/services/combo/quotaExhaustionCutoff.ts +140 -0
  812. package/open-sse/services/combo/quotaScoring.ts +311 -0
  813. package/open-sse/services/combo/quotaShareConcurrency.ts +74 -0
  814. package/open-sse/services/combo/quotaShareInflight.ts +133 -0
  815. package/open-sse/services/combo/quotaShareStrategy.ts +349 -0
  816. package/open-sse/services/combo/quotaStrategies.ts +647 -0
  817. package/open-sse/services/combo/resolveAutoStrategy.ts +334 -0
  818. package/open-sse/services/combo/responseValidation.ts +206 -0
  819. package/open-sse/services/combo/rrState.ts +98 -0
  820. package/open-sse/services/combo/runtimeUnits.ts +269 -0
  821. package/open-sse/services/combo/sessionStickiness.ts +301 -0
  822. package/open-sse/services/combo/shadowRouting.ts +179 -0
  823. package/open-sse/services/combo/targetExhaustion.ts +160 -0
  824. package/open-sse/services/combo/targetSorters.ts +174 -0
  825. package/open-sse/services/combo/targetTimeoutRunner.ts +91 -0
  826. package/open-sse/services/combo/types.ts +175 -0
  827. package/open-sse/services/combo/validateQuality.ts +445 -0
  828. package/open-sse/services/combo.ts +3085 -0
  829. package/open-sse/services/comboAgentMiddleware.ts +214 -0
  830. package/open-sse/services/comboConfig.ts +248 -0
  831. package/open-sse/services/comboManifestMetrics.ts +13 -0
  832. package/open-sse/services/comboMetrics.ts +480 -0
  833. package/open-sse/services/compression/adaptiveCompression/computeTarget.ts +31 -0
  834. package/open-sse/services/compression/adaptiveCompression/ladder.ts +59 -0
  835. package/open-sse/services/compression/adaptiveCompression/resolveAdaptivePlan.ts +104 -0
  836. package/open-sse/services/compression/adaptiveCompression/types.ts +61 -0
  837. package/open-sse/services/compression/aggressive.ts +204 -0
  838. package/open-sse/services/compression/bodyAdapter.ts +354 -0
  839. package/open-sse/services/compression/cacheAwareConfig.ts +60 -0
  840. package/open-sse/services/compression/cachingAware.ts +130 -0
  841. package/open-sse/services/compression/caveman.ts +602 -0
  842. package/open-sse/services/compression/cavemanRules.ts +432 -0
  843. package/open-sse/services/compression/deriveDefaultPlan.ts +59 -0
  844. package/open-sse/services/compression/diffHelper.ts +219 -0
  845. package/open-sse/services/compression/engineBreakdown.ts +29 -0
  846. package/open-sse/services/compression/engineCatalog.ts +91 -0
  847. package/open-sse/services/compression/engines/cavemanAdapter.ts +454 -0
  848. package/open-sse/services/compression/engines/ccr/ccrQuery.ts +110 -0
  849. package/open-sse/services/compression/engines/ccr/index.ts +460 -0
  850. package/open-sse/services/compression/engines/headroom/encoderComparison.ts +72 -0
  851. package/open-sse/services/compression/engines/headroom/gcf/decode_generic.ts +711 -0
  852. package/open-sse/services/compression/engines/headroom/gcf/generic.ts +332 -0
  853. package/open-sse/services/compression/engines/headroom/gcf/index.ts +9 -0
  854. package/open-sse/services/compression/engines/headroom/gcf/scalar.ts +336 -0
  855. package/open-sse/services/compression/engines/headroom/index.ts +286 -0
  856. package/open-sse/services/compression/engines/headroom/smartcrusher.ts +255 -0
  857. package/open-sse/services/compression/engines/headroom/tabular.ts +263 -0
  858. package/open-sse/services/compression/engines/headroom/toon.ts +43 -0
  859. package/open-sse/services/compression/engines/index.ts +42 -0
  860. package/open-sse/services/compression/engines/ionizer/index.ts +124 -0
  861. package/open-sse/services/compression/engines/ionizer/sample.ts +200 -0
  862. package/open-sse/services/compression/engines/llm/index.ts +346 -0
  863. package/open-sse/services/compression/engines/llmlingua/constants.ts +55 -0
  864. package/open-sse/services/compression/engines/llmlingua/index.ts +474 -0
  865. package/open-sse/services/compression/engines/llmlingua/modelStore.ts +67 -0
  866. package/open-sse/services/compression/engines/llmlingua/onnxWorker.ts +117 -0
  867. package/open-sse/services/compression/engines/llmlingua/ultraEntry.ts +109 -0
  868. package/open-sse/services/compression/engines/llmlingua/worker.ts +373 -0
  869. package/open-sse/services/compression/engines/mcpAccessibility/collapseRepeated.ts +121 -0
  870. package/open-sse/services/compression/engines/mcpAccessibility/constants.ts +63 -0
  871. package/open-sse/services/compression/engines/mcpAccessibility/index.ts +52 -0
  872. package/open-sse/services/compression/engines/readLifecycle/index.ts +299 -0
  873. package/open-sse/services/compression/engines/registry.ts +87 -0
  874. package/open-sse/services/compression/engines/relevance/configSchema.ts +63 -0
  875. package/open-sse/services/compression/engines/relevance/index.ts +185 -0
  876. package/open-sse/services/compression/engines/relevance/scorer.ts +85 -0
  877. package/open-sse/services/compression/engines/rtk/codeStripper.ts +145 -0
  878. package/open-sse/services/compression/engines/rtk/commandDetector.ts +482 -0
  879. package/open-sse/services/compression/engines/rtk/configSchema.ts +117 -0
  880. package/open-sse/services/compression/engines/rtk/deduplicator.ts +37 -0
  881. package/open-sse/services/compression/engines/rtk/discover.ts +150 -0
  882. package/open-sse/services/compression/engines/rtk/filterLoader.ts +245 -0
  883. package/open-sse/services/compression/engines/rtk/filterSchema.ts +207 -0
  884. package/open-sse/services/compression/engines/rtk/filters/aws.json +34 -0
  885. package/open-sse/services/compression/engines/rtk/filters/biome.json +35 -0
  886. package/open-sse/services/compression/engines/rtk/filters/build-eslint.json +37 -0
  887. package/open-sse/services/compression/engines/rtk/filters/build-typescript.json +33 -0
  888. package/open-sse/services/compression/engines/rtk/filters/build-vite.json +41 -0
  889. package/open-sse/services/compression/engines/rtk/filters/build-webpack.json +34 -0
  890. package/open-sse/services/compression/engines/rtk/filters/bundle-install.json +35 -0
  891. package/open-sse/services/compression/engines/rtk/filters/composer.json +81 -0
  892. package/open-sse/services/compression/engines/rtk/filters/curl.json +34 -0
  893. package/open-sse/services/compression/engines/rtk/filters/df.json +34 -0
  894. package/open-sse/services/compression/engines/rtk/filters/docker-build.json +78 -0
  895. package/open-sse/services/compression/engines/rtk/filters/docker-logs.json +42 -0
  896. package/open-sse/services/compression/engines/rtk/filters/docker-ps.json +33 -0
  897. package/open-sse/services/compression/engines/rtk/filters/dotnet.json +47 -0
  898. package/open-sse/services/compression/engines/rtk/filters/du.json +33 -0
  899. package/open-sse/services/compression/engines/rtk/filters/error-stacktrace.json +41 -0
  900. package/open-sse/services/compression/engines/rtk/filters/gcloud.json +34 -0
  901. package/open-sse/services/compression/engines/rtk/filters/generic-output.json +32 -0
  902. package/open-sse/services/compression/engines/rtk/filters/gh.json +98 -0
  903. package/open-sse/services/compression/engines/rtk/filters/git-branch.json +45 -0
  904. package/open-sse/services/compression/engines/rtk/filters/git-diff.json +33 -0
  905. package/open-sse/services/compression/engines/rtk/filters/git-log.json +33 -0
  906. package/open-sse/services/compression/engines/rtk/filters/git-status.json +40 -0
  907. package/open-sse/services/compression/engines/rtk/filters/golangci-lint.json +34 -0
  908. package/open-sse/services/compression/engines/rtk/filters/gradle.json +47 -0
  909. package/open-sse/services/compression/engines/rtk/filters/json-output.json +40 -0
  910. package/open-sse/services/compression/engines/rtk/filters/kubectl.json +99 -0
  911. package/open-sse/services/compression/engines/rtk/filters/make.json +38 -0
  912. package/open-sse/services/compression/engines/rtk/filters/mypy.json +34 -0
  913. package/open-sse/services/compression/engines/rtk/filters/npm-audit.json +43 -0
  914. package/open-sse/services/compression/engines/rtk/filters/npm-install.json +41 -0
  915. package/open-sse/services/compression/engines/rtk/filters/nx.json +34 -0
  916. package/open-sse/services/compression/engines/rtk/filters/pip.json +41 -0
  917. package/open-sse/services/compression/engines/rtk/filters/playwright.json +42 -0
  918. package/open-sse/services/compression/engines/rtk/filters/poetry-install.json +35 -0
  919. package/open-sse/services/compression/engines/rtk/filters/prettier.json +40 -0
  920. package/open-sse/services/compression/engines/rtk/filters/ps.json +34 -0
  921. package/open-sse/services/compression/engines/rtk/filters/rsync.json +33 -0
  922. package/open-sse/services/compression/engines/rtk/filters/rubocop.json +34 -0
  923. package/open-sse/services/compression/engines/rtk/filters/ruff.json +40 -0
  924. package/open-sse/services/compression/engines/rtk/filters/shell-find.json +33 -0
  925. package/open-sse/services/compression/engines/rtk/filters/shell-grep.json +37 -0
  926. package/open-sse/services/compression/engines/rtk/filters/shell-ls.json +33 -0
  927. package/open-sse/services/compression/engines/rtk/filters/ssh.json +43 -0
  928. package/open-sse/services/compression/engines/rtk/filters/systemctl-status.json +43 -0
  929. package/open-sse/services/compression/engines/rtk/filters/terraform-plan.json +34 -0
  930. package/open-sse/services/compression/engines/rtk/filters/test-cargo.json +42 -0
  931. package/open-sse/services/compression/engines/rtk/filters/test-go.json +41 -0
  932. package/open-sse/services/compression/engines/rtk/filters/test-jest.json +43 -0
  933. package/open-sse/services/compression/engines/rtk/filters/test-pytest.json +41 -0
  934. package/open-sse/services/compression/engines/rtk/filters/test-vitest.json +43 -0
  935. package/open-sse/services/compression/engines/rtk/filters/tofu-plan.json +34 -0
  936. package/open-sse/services/compression/engines/rtk/filters/turbo.json +34 -0
  937. package/open-sse/services/compression/engines/rtk/filters/uv-sync.json +35 -0
  938. package/open-sse/services/compression/engines/rtk/filters/wget.json +34 -0
  939. package/open-sse/services/compression/engines/rtk/grouper.ts +99 -0
  940. package/open-sse/services/compression/engines/rtk/index.ts +706 -0
  941. package/open-sse/services/compression/engines/rtk/learn.ts +290 -0
  942. package/open-sse/services/compression/engines/rtk/lineFilter.ts +178 -0
  943. package/open-sse/services/compression/engines/rtk/rawOutput.ts +208 -0
  944. package/open-sse/services/compression/engines/rtk/renderers/gitDiff.ts +39 -0
  945. package/open-sse/services/compression/engines/rtk/renderers/index.ts +50 -0
  946. package/open-sse/services/compression/engines/rtk/renderers/structuredTable.ts +96 -0
  947. package/open-sse/services/compression/engines/rtk/renderers/terraformPlan.ts +40 -0
  948. package/open-sse/services/compression/engines/rtk/renderers/testGreen.ts +70 -0
  949. package/open-sse/services/compression/engines/rtk/renderers/types.ts +13 -0
  950. package/open-sse/services/compression/engines/rtk/smartTruncate.ts +67 -0
  951. package/open-sse/services/compression/engines/rtk/splitCompositeCommand.ts +111 -0
  952. package/open-sse/services/compression/engines/rtk/verify.ts +107 -0
  953. package/open-sse/services/compression/engines/session-dedup/fuzzy.ts +153 -0
  954. package/open-sse/services/compression/engines/session-dedup/index.ts +435 -0
  955. package/open-sse/services/compression/engines/types.ts +77 -0
  956. package/open-sse/services/compression/entrypointWrap.ts +43 -0
  957. package/open-sse/services/compression/eval/aggregate.ts +62 -0
  958. package/open-sse/services/compression/eval/corpus.ts +45 -0
  959. package/open-sse/services/compression/eval/costMeter.ts +31 -0
  960. package/open-sse/services/compression/eval/executorModelClient.ts +44 -0
  961. package/open-sse/services/compression/eval/fidelityCheck.ts +61 -0
  962. package/open-sse/services/compression/eval/grader.ts +27 -0
  963. package/open-sse/services/compression/eval/judge.ts +68 -0
  964. package/open-sse/services/compression/eval/report.ts +44 -0
  965. package/open-sse/services/compression/eval/runner.ts +135 -0
  966. package/open-sse/services/compression/eval/savings.ts +30 -0
  967. package/open-sse/services/compression/eval/seedCorpus.ts +60 -0
  968. package/open-sse/services/compression/eval/types.ts +65 -0
  969. package/open-sse/services/compression/fidelityGate.ts +124 -0
  970. package/open-sse/services/compression/fidelityGateStep.ts +44 -0
  971. package/open-sse/services/compression/hardBudget.ts +196 -0
  972. package/open-sse/services/compression/harness/benchmark.ts +318 -0
  973. package/open-sse/services/compression/harness/budgetGate.ts +62 -0
  974. package/open-sse/services/compression/harness/index.ts +52 -0
  975. package/open-sse/services/compression/harness/measure.ts +98 -0
  976. package/open-sse/services/compression/harness/replay.ts +72 -0
  977. package/open-sse/services/compression/harness/runner.ts +58 -0
  978. package/open-sse/services/compression/index.ts +165 -0
  979. package/open-sse/services/compression/languageDetector.ts +51 -0
  980. package/open-sse/services/compression/lite.ts +241 -0
  981. package/open-sse/services/compression/messageContent.ts +89 -0
  982. package/open-sse/services/compression/outputMode.ts +175 -0
  983. package/open-sse/services/compression/outputStyles/apply.ts +135 -0
  984. package/open-sse/services/compression/outputStyles/backCompat.ts +29 -0
  985. package/open-sse/services/compression/outputStyles/catalog.ts +76 -0
  986. package/open-sse/services/compression/outputStyles/telemetry.ts +50 -0
  987. package/open-sse/services/compression/pipelineEngineBreaker.ts +139 -0
  988. package/open-sse/services/compression/pipelineGuards.ts +45 -0
  989. package/open-sse/services/compression/planResolution.ts +128 -0
  990. package/open-sse/services/compression/prefixFreeze.ts +124 -0
  991. package/open-sse/services/compression/preservation.ts +0 -0
  992. package/open-sse/services/compression/preserveSystemPromptMode.ts +72 -0
  993. package/open-sse/services/compression/progressiveAging.ts +145 -0
  994. package/open-sse/services/compression/quantumLock/index.ts +17 -0
  995. package/open-sse/services/compression/quantumLock/quantumLock.ts +58 -0
  996. package/open-sse/services/compression/quantumLock/quantumLockStep.ts +72 -0
  997. package/open-sse/services/compression/quantumLock/quantumPatterns.ts +80 -0
  998. package/open-sse/services/compression/quantumLock/strategyWrap.ts +72 -0
  999. package/open-sse/services/compression/resolveCompressionPlan.ts +28 -0
  1000. package/open-sse/services/compression/resultMemo.ts +83 -0
  1001. package/open-sse/services/compression/riskGate/index.ts +3 -0
  1002. package/open-sse/services/compression/riskGate/riskGate.ts +136 -0
  1003. package/open-sse/services/compression/riskGate/riskGateStep.ts +115 -0
  1004. package/open-sse/services/compression/riskGate/riskPatterns.ts +61 -0
  1005. package/open-sse/services/compression/riskGate/strategyWrap.ts +44 -0
  1006. package/open-sse/services/compression/ruleLoader.ts +273 -0
  1007. package/open-sse/services/compression/rules/_schema.json +31 -0
  1008. package/open-sse/services/compression/rules/de/context.json +38 -0
  1009. package/open-sse/services/compression/rules/de/dedup.json +38 -0
  1010. package/open-sse/services/compression/rules/de/filler.json +70 -0
  1011. package/open-sse/services/compression/rules/de/structural.json +30 -0
  1012. package/open-sse/services/compression/rules/de/ultra.json +107 -0
  1013. package/open-sse/services/compression/rules/en/context.json +88 -0
  1014. package/open-sse/services/compression/rules/en/dedup.json +38 -0
  1015. package/open-sse/services/compression/rules/en/filler.json +129 -0
  1016. package/open-sse/services/compression/rules/en/structural.json +102 -0
  1017. package/open-sse/services/compression/rules/en/ultra.json +107 -0
  1018. package/open-sse/services/compression/rules/es/context.json +87 -0
  1019. package/open-sse/services/compression/rules/es/dedup.json +38 -0
  1020. package/open-sse/services/compression/rules/es/filler.json +110 -0
  1021. package/open-sse/services/compression/rules/es/structural.json +105 -0
  1022. package/open-sse/services/compression/rules/es/ultra.json +107 -0
  1023. package/open-sse/services/compression/rules/fr/context.json +38 -0
  1024. package/open-sse/services/compression/rules/fr/dedup.json +38 -0
  1025. package/open-sse/services/compression/rules/fr/filler.json +70 -0
  1026. package/open-sse/services/compression/rules/fr/structural.json +30 -0
  1027. package/open-sse/services/compression/rules/fr/ultra.json +107 -0
  1028. package/open-sse/services/compression/rules/id/context.json +113 -0
  1029. package/open-sse/services/compression/rules/id/dedup.json +38 -0
  1030. package/open-sse/services/compression/rules/id/filler.json +138 -0
  1031. package/open-sse/services/compression/rules/id/structural.json +111 -0
  1032. package/open-sse/services/compression/rules/id/ultra.json +252 -0
  1033. package/open-sse/services/compression/rules/ja/context.json +38 -0
  1034. package/open-sse/services/compression/rules/ja/dedup.json +38 -0
  1035. package/open-sse/services/compression/rules/ja/filler.json +70 -0
  1036. package/open-sse/services/compression/rules/ja/structural.json +30 -0
  1037. package/open-sse/services/compression/rules/ja/ultra.json +86 -0
  1038. package/open-sse/services/compression/rules/pt-BR/context.json +69 -0
  1039. package/open-sse/services/compression/rules/pt-BR/dedup.json +51 -0
  1040. package/open-sse/services/compression/rules/pt-BR/filler.json +151 -0
  1041. package/open-sse/services/compression/rules/pt-BR/structural.json +78 -0
  1042. package/open-sse/services/compression/rules/pt-BR/ultra.json +192 -0
  1043. package/open-sse/services/compression/rules/zh/dedup.json +38 -0
  1044. package/open-sse/services/compression/rules/zh/filler.json +54 -0
  1045. package/open-sse/services/compression/rules/zh/ultra.json +55 -0
  1046. package/open-sse/services/compression/stackedStepCore.ts +87 -0
  1047. package/open-sse/services/compression/stats.ts +59 -0
  1048. package/open-sse/services/compression/strategySelector.ts +1007 -0
  1049. package/open-sse/services/compression/summarizer.ts +244 -0
  1050. package/open-sse/services/compression/toolResultCompressor.ts +283 -0
  1051. package/open-sse/services/compression/types.ts +479 -0
  1052. package/open-sse/services/compression/ultra.ts +321 -0
  1053. package/open-sse/services/compression/ultraHeuristic.ts +157 -0
  1054. package/open-sse/services/compression/validation.ts +432 -0
  1055. package/open-sse/services/contextHandoff.ts +770 -0
  1056. package/open-sse/services/contextManager.ts +617 -0
  1057. package/open-sse/services/credentialGate.ts +84 -0
  1058. package/open-sse/services/crofUsageFetcher.ts +182 -0
  1059. package/open-sse/services/cursorSessionManager.ts +208 -0
  1060. package/open-sse/services/deepseekQuotaFetcher.ts +244 -0
  1061. package/open-sse/services/deviceTracker.ts +307 -0
  1062. package/open-sse/services/emergencyFallback.ts +162 -0
  1063. package/open-sse/services/errorClassifier.ts +198 -0
  1064. package/open-sse/services/evalRouting.ts +276 -0
  1065. package/open-sse/services/freeWebSearch.ts +154 -0
  1066. package/open-sse/services/fusion.ts +337 -0
  1067. package/open-sse/services/geminiRateLimitTracker.ts +145 -0
  1068. package/open-sse/services/geminiThoughtSignatureStore.ts +329 -0
  1069. package/open-sse/services/genericQuotaFetcher.ts +215 -0
  1070. package/open-sse/services/gigachatAuth.ts +113 -0
  1071. package/open-sse/services/githubCopilotModels.ts +162 -0
  1072. package/open-sse/services/gpt5SamplingGuard.ts +81 -0
  1073. package/open-sse/services/grokTlsClient.ts +607 -0
  1074. package/open-sse/services/hfModelSuggestions.ts +63 -0
  1075. package/open-sse/services/inAppLoginService.ts +256 -0
  1076. package/open-sse/services/intentClassifier.ts +665 -0
  1077. package/open-sse/services/ipFilter.ts +340 -0
  1078. package/open-sse/services/keyGroupAuth.ts +84 -0
  1079. package/open-sse/services/kiroModels.ts +195 -0
  1080. package/open-sse/services/manifestAdapter.ts +134 -0
  1081. package/open-sse/services/mimoThinking.ts +54 -0
  1082. package/open-sse/services/model.ts +738 -0
  1083. package/open-sse/services/modelCapabilities.ts +5 -0
  1084. package/open-sse/services/modelDeprecation.ts +173 -0
  1085. package/open-sse/services/modelFamilyFallback.ts +240 -0
  1086. package/open-sse/services/modelStrip.ts +60 -0
  1087. package/open-sse/services/modelscopePolicy.ts +97 -0
  1088. package/open-sse/services/opencodeOllamaUsage.ts +562 -0
  1089. package/open-sse/services/opencodeQuotaFetcher.ts +351 -0
  1090. package/open-sse/services/payloadRules.ts +474 -0
  1091. package/open-sse/services/perplexityTlsClient.ts +593 -0
  1092. package/open-sse/services/pipeline.ts +189 -0
  1093. package/open-sse/services/provider.ts +460 -0
  1094. package/open-sse/services/providerCooldownTracker.ts +198 -0
  1095. package/open-sse/services/providerCostData.ts +52 -0
  1096. package/open-sse/services/providerDefaultRateLimit.ts +94 -0
  1097. package/open-sse/services/providerRequestDefaults.ts +88 -0
  1098. package/open-sse/services/proxyAutoSelector.ts +58 -0
  1099. package/open-sse/services/qoderCli.ts +987 -0
  1100. package/open-sse/services/qoderCliResolve.ts +111 -0
  1101. package/open-sse/services/quotaFetchThrottle.ts +118 -0
  1102. package/open-sse/services/quotaMonitor.ts +377 -0
  1103. package/open-sse/services/quotaPreflight.ts +349 -0
  1104. package/open-sse/services/qwenThinking.ts +44 -0
  1105. package/open-sse/services/rateLimitManager/headers.ts +94 -0
  1106. package/open-sse/services/rateLimitManager.ts +945 -0
  1107. package/open-sse/services/rateLimitSemaphore.ts +231 -0
  1108. package/open-sse/services/reasoningCache.ts +514 -0
  1109. package/open-sse/services/reasoningTokenBuffer.ts +50 -0
  1110. package/open-sse/services/refreshSerializer.ts +131 -0
  1111. package/open-sse/services/requestDedup.ts +127 -0
  1112. package/open-sse/services/responseModelEcho.ts +79 -0
  1113. package/open-sse/services/responsesInputSanitizer.ts +145 -0
  1114. package/open-sse/services/retryAfterJson.ts +44 -0
  1115. package/open-sse/services/roleNormalizer.ts +283 -0
  1116. package/open-sse/services/searchCache.ts +150 -0
  1117. package/open-sse/services/sessionManager.ts +380 -0
  1118. package/open-sse/services/sessionPool/fingerprintRotator.ts +126 -0
  1119. package/open-sse/services/sessionPool/index.ts +28 -0
  1120. package/open-sse/services/sessionPool/poolRegistry.ts +93 -0
  1121. package/open-sse/services/sessionPool/session.ts +119 -0
  1122. package/open-sse/services/sessionPool/sessionFactory.ts +57 -0
  1123. package/open-sse/services/sessionPool/sessionPool.ts +326 -0
  1124. package/open-sse/services/sessionPool/types.ts +100 -0
  1125. package/open-sse/services/sessionPool/webExecutorWrapper.ts +132 -0
  1126. package/open-sse/services/signatureCache.ts +189 -0
  1127. package/open-sse/services/slidingWindowLimiter.ts +84 -0
  1128. package/open-sse/services/specificityDetector.ts +89 -0
  1129. package/open-sse/services/specificityRules.ts +257 -0
  1130. package/open-sse/services/specificityTypes.ts +43 -0
  1131. package/open-sse/services/streamRecovery.ts +545 -0
  1132. package/open-sse/services/systemPrompt.ts +187 -0
  1133. package/open-sse/services/systemTransforms.ts +532 -0
  1134. package/open-sse/services/taskAwareRouter.ts +326 -0
  1135. package/open-sse/services/taskAwareRouting.ts +553 -0
  1136. package/open-sse/services/thinkingBudget.ts +391 -0
  1137. package/open-sse/services/tierConfig.ts +124 -0
  1138. package/open-sse/services/tierDefaults.json +26 -0
  1139. package/open-sse/services/tierResolver.ts +144 -0
  1140. package/open-sse/services/tierTypes.ts +40 -0
  1141. package/open-sse/services/tlsClientProxy.ts +30 -0
  1142. package/open-sse/services/tokenExtractionConfig.ts +398 -0
  1143. package/open-sse/services/tokenLimitCounter.ts +342 -0
  1144. package/open-sse/services/tokenRefresh.ts +2180 -0
  1145. package/open-sse/services/toolLatencyTracker.ts +89 -0
  1146. package/open-sse/services/toolLimitDetector.ts +87 -0
  1147. package/open-sse/services/toolSchemaSanitizer.ts +165 -0
  1148. package/open-sse/services/usage/antigravity.ts +794 -0
  1149. package/open-sse/services/usage/claude.ts +216 -0
  1150. package/open-sse/services/usage/codebuddy-cn.ts +199 -0
  1151. package/open-sse/services/usage/codex.ts +75 -0
  1152. package/open-sse/services/usage/cursor.ts +161 -0
  1153. package/open-sse/services/usage/glm.ts +220 -0
  1154. package/open-sse/services/usage/kimi.ts +209 -0
  1155. package/open-sse/services/usage/kiro.ts +243 -0
  1156. package/open-sse/services/usage/minimax.ts +346 -0
  1157. package/open-sse/services/usage/quota.ts +85 -0
  1158. package/open-sse/services/usage/scalars.ts +75 -0
  1159. package/open-sse/services/usage.ts +1058 -0
  1160. package/open-sse/services/volumeDetector.ts +224 -0
  1161. package/open-sse/services/webSearchFallback.ts +246 -0
  1162. package/open-sse/services/webSearchRouting.ts +68 -0
  1163. package/open-sse/services/webSessionPoolHealth.ts +287 -0
  1164. package/open-sse/services/wildcardRouter.ts +136 -0
  1165. package/open-sse/services/workflowFSM.ts +340 -0
  1166. package/open-sse/shared/zedAuth.ts +535 -0
  1167. package/open-sse/transformer/responsesTransformer.ts +667 -0
  1168. package/open-sse/translator/bootstrap.ts +27 -0
  1169. package/open-sse/translator/deepseekWebTools.ts +486 -0
  1170. package/open-sse/translator/formats.ts +12 -0
  1171. package/open-sse/translator/helpers/claudeHelper.ts +563 -0
  1172. package/open-sse/translator/helpers/geminiHelper.ts +657 -0
  1173. package/open-sse/translator/helpers/geminiToolsSanitizer.ts +255 -0
  1174. package/open-sse/translator/helpers/jsonUtil.ts +18 -0
  1175. package/open-sse/translator/helpers/maxTokensHelper.ts +21 -0
  1176. package/open-sse/translator/helpers/openaiHelper.ts +244 -0
  1177. package/open-sse/translator/helpers/responsesApiHelper.ts +9 -0
  1178. package/open-sse/translator/helpers/schemaCoercion.ts +408 -0
  1179. package/open-sse/translator/helpers/toolCallHelper.ts +224 -0
  1180. package/open-sse/translator/helpers/toolCallShim.ts +113 -0
  1181. package/open-sse/translator/image/sizeMapper.ts +22 -0
  1182. package/open-sse/translator/index.ts +614 -0
  1183. package/open-sse/translator/paramSupport.ts +70 -0
  1184. package/open-sse/translator/registry.ts +41 -0
  1185. package/open-sse/translator/request/antigravity-to-openai.ts +357 -0
  1186. package/open-sse/translator/request/claude-to-gemini.ts +236 -0
  1187. package/open-sse/translator/request/claude-to-openai.ts +534 -0
  1188. package/open-sse/translator/request/gemini-to-openai.ts +193 -0
  1189. package/open-sse/translator/request/openai-responses/helpers.ts +69 -0
  1190. package/open-sse/translator/request/openai-responses/toResponses.ts +357 -0
  1191. package/open-sse/translator/request/openai-responses.ts +533 -0
  1192. package/open-sse/translator/request/openai-to-claude/thinkingBudget.ts +89 -0
  1193. package/open-sse/translator/request/openai-to-claude/toolResultAdjacency.ts +106 -0
  1194. package/open-sse/translator/request/openai-to-claude.ts +747 -0
  1195. package/open-sse/translator/request/openai-to-cursor.ts +226 -0
  1196. package/open-sse/translator/request/openai-to-gemini/helpers.ts +142 -0
  1197. package/open-sse/translator/request/openai-to-gemini.ts +753 -0
  1198. package/open-sse/translator/request/openai-to-kiro/messageHelpers.ts +109 -0
  1199. package/open-sse/translator/request/openai-to-kiro.ts +889 -0
  1200. package/open-sse/translator/response/claude-to-openai.ts +327 -0
  1201. package/open-sse/translator/response/cursor-to-openai.ts +30 -0
  1202. package/open-sse/translator/response/gemini-to-claude.ts +199 -0
  1203. package/open-sse/translator/response/gemini-to-openai.ts +765 -0
  1204. package/open-sse/translator/response/kiro-to-openai.ts +211 -0
  1205. package/open-sse/translator/response/openai-responses/pureHelpers.ts +92 -0
  1206. package/open-sse/translator/response/openai-responses.ts +1011 -0
  1207. package/open-sse/translator/response/openai-to-antigravity.ts +145 -0
  1208. package/open-sse/translator/response/openai-to-claude.ts +289 -0
  1209. package/open-sse/translator/response/openai-to-gemini-sse.ts +431 -0
  1210. package/open-sse/translator/webTools.ts +531 -0
  1211. package/open-sse/tsconfig.json +25 -0
  1212. package/open-sse/types.d.ts +139 -0
  1213. package/open-sse/utils/agentGoalPolicy.ts +112 -0
  1214. package/open-sse/utils/aiSdkCompat.ts +179 -0
  1215. package/open-sse/utils/anthropicHost.ts +25 -0
  1216. package/open-sse/utils/awsSigV4.ts +139 -0
  1217. package/open-sse/utils/bypassHandler.ts +300 -0
  1218. package/open-sse/utils/cacheControlPolicy.ts +374 -0
  1219. package/open-sse/utils/claudeCodeMetaRequests.ts +87 -0
  1220. package/open-sse/utils/clinepassEnvelope.ts +59 -0
  1221. package/open-sse/utils/comfyuiClient.ts +126 -0
  1222. package/open-sse/utils/composerToolCalls.ts +307 -0
  1223. package/open-sse/utils/cors.ts +13 -0
  1224. package/open-sse/utils/cursorAgentProtobuf/wire.ts +143 -0
  1225. package/open-sse/utils/cursorAgentProtobuf.ts +1400 -0
  1226. package/open-sse/utils/cursorChecksum.ts +139 -0
  1227. package/open-sse/utils/cursorImages.ts +354 -0
  1228. package/open-sse/utils/cursorVersionDetector.ts +74 -0
  1229. package/open-sse/utils/diagnostics.ts +288 -0
  1230. package/open-sse/utils/earlyStreamKeepalive.ts +228 -0
  1231. package/open-sse/utils/error.ts +515 -0
  1232. package/open-sse/utils/estimateSize.ts +35 -0
  1233. package/open-sse/utils/finishReason.ts +36 -0
  1234. package/open-sse/utils/flattenToolHistory.ts +116 -0
  1235. package/open-sse/utils/headers.ts +61 -0
  1236. package/open-sse/utils/heapPressure.ts +81 -0
  1237. package/open-sse/utils/jsonToSse.ts +127 -0
  1238. package/open-sse/utils/keepaliveThreshold.ts +62 -0
  1239. package/open-sse/utils/kieTask.ts +127 -0
  1240. package/open-sse/utils/kiroSanitizer.ts +127 -0
  1241. package/open-sse/utils/logger.ts +190 -0
  1242. package/open-sse/utils/networkProxy.ts +75 -0
  1243. package/open-sse/utils/noThinkingAlias.ts +153 -0
  1244. package/open-sse/utils/number.ts +4 -0
  1245. package/open-sse/utils/ollamaTransform.ts +122 -0
  1246. package/open-sse/utils/opencodeHeaders.ts +98 -0
  1247. package/open-sse/utils/passthroughTailProcessor.ts +299 -0
  1248. package/open-sse/utils/progressTracker.ts +112 -0
  1249. package/open-sse/utils/providerRequestLogging.ts +272 -0
  1250. package/open-sse/utils/proxyDispatcher.ts +507 -0
  1251. package/open-sse/utils/proxyDispatcherCache.ts +124 -0
  1252. package/open-sse/utils/proxyFallback.ts +418 -0
  1253. package/open-sse/utils/proxyFamily.ts +24 -0
  1254. package/open-sse/utils/proxyFamilyResolve.ts +39 -0
  1255. package/open-sse/utils/proxyFetch.ts +666 -0
  1256. package/open-sse/utils/publicCreds.ts +215 -0
  1257. package/open-sse/utils/reasoningContentInjector.ts +73 -0
  1258. package/open-sse/utils/reasoningFields.ts +72 -0
  1259. package/open-sse/utils/requestLogger.ts +386 -0
  1260. package/open-sse/utils/responsesInputNormalization.ts +94 -0
  1261. package/open-sse/utils/responsesStatePolicy.ts +75 -0
  1262. package/open-sse/utils/responsesStreamHelpers.ts +166 -0
  1263. package/open-sse/utils/setupPolyfill.ts +33 -0
  1264. package/open-sse/utils/sha3-512.ts +164 -0
  1265. package/open-sse/utils/sleep.ts +9 -0
  1266. package/open-sse/utils/socksConnectorWithFamily.ts +89 -0
  1267. package/open-sse/utils/sseHeartbeat.ts +119 -0
  1268. package/open-sse/utils/stream.ts +2791 -0
  1269. package/open-sse/utils/streamFailureFinalization.ts +174 -0
  1270. package/open-sse/utils/streamHandler.ts +680 -0
  1271. package/open-sse/utils/streamHelpers.ts +460 -0
  1272. package/open-sse/utils/streamPayloadCollector.ts +700 -0
  1273. package/open-sse/utils/streamReadiness.ts +385 -0
  1274. package/open-sse/utils/streamReadinessPolicy.ts +132 -0
  1275. package/open-sse/utils/textualToolCall.ts +112 -0
  1276. package/open-sse/utils/thinkCloseMarker.ts +77 -0
  1277. package/open-sse/utils/thinkTagParser.ts +146 -0
  1278. package/open-sse/utils/tlsClient.ts +243 -0
  1279. package/open-sse/utils/toolCallArguments.ts +32 -0
  1280. package/open-sse/utils/toolSources.ts +69 -0
  1281. package/open-sse/utils/upstreamResponseHeaders.ts +47 -0
  1282. package/open-sse/utils/urlSanitize.ts +24 -0
  1283. package/open-sse/utils/usageTracking.ts +596 -0
  1284. package/package.json +393 -0
  1285. package/scripts/build/build-next-isolated.mjs +322 -0
  1286. package/scripts/build/colocateOptionals.mjs +144 -0
  1287. package/scripts/build/native-binary-compat.mjs +167 -0
  1288. package/scripts/build/postinstall.mjs +357 -0
  1289. package/scripts/build/postinstallSupport.mjs +45 -0
  1290. package/scripts/build/runtime-env.mjs +140 -0
  1291. package/scripts/build/sync-env.mjs +3 -0
  1292. package/scripts/check/check-supported-node-runtime.ts +20 -0
  1293. package/scripts/dev/responses-ws-proxy.mjs +907 -0
  1294. package/scripts/dev/sync-env.mjs +362 -0
  1295. package/scripts/dev/tls-options.mjs +92 -0
  1296. package/scripts/postinstall.mjs +27 -0
  1297. package/src/domain/assessment/assessor.ts +206 -0
  1298. package/src/domain/assessment/categorizer.ts +118 -0
  1299. package/src/domain/assessment/index.ts +24 -0
  1300. package/src/domain/assessment/migration.ts +111 -0
  1301. package/src/domain/assessment/selfHealer.ts +261 -0
  1302. package/src/domain/assessment/types.ts +361 -0
  1303. package/src/domain/comboResolver.ts +106 -0
  1304. package/src/domain/configAudit.ts +285 -0
  1305. package/src/domain/connectionModelRules.ts +82 -0
  1306. package/src/domain/costRules.ts +631 -0
  1307. package/src/domain/degradation.ts +253 -0
  1308. package/src/domain/fallbackPolicy.ts +160 -0
  1309. package/src/domain/lockoutPolicy.ts +208 -0
  1310. package/src/domain/modelAvailability.ts +41 -0
  1311. package/src/domain/omnirouteResponseMeta.ts +202 -0
  1312. package/src/domain/pipeline.ts +307 -0
  1313. package/src/domain/policyEngine.ts +195 -0
  1314. package/src/domain/prompts.ts +103 -0
  1315. package/src/domain/providerExpiration.ts +255 -0
  1316. package/src/domain/quotaCache.ts +553 -0
  1317. package/src/domain/responses.ts +139 -0
  1318. package/src/domain/tagRouter.ts +64 -0
  1319. package/src/domain/types.ts +123 -0
  1320. package/src/lib/a2a/README.md +748 -0
  1321. package/src/lib/a2a/routingLogger.ts +67 -0
  1322. package/src/lib/a2a/skills/costAnalysis.ts +162 -0
  1323. package/src/lib/a2a/skills/healthReport.ts +155 -0
  1324. package/src/lib/a2a/skills/listCapabilities.ts +72 -0
  1325. package/src/lib/a2a/skills/providerDiscovery.ts +202 -0
  1326. package/src/lib/a2a/skills/quotaManagement.ts +121 -0
  1327. package/src/lib/a2a/skills/smartRouting.ts +89 -0
  1328. package/src/lib/a2a/streaming.ts +149 -0
  1329. package/src/lib/a2a/taskExecution.ts +64 -0
  1330. package/src/lib/a2a/taskManager.ts +240 -0
  1331. package/src/lib/accessTokens/scopes.ts +51 -0
  1332. package/src/lib/acp/index.ts +11 -0
  1333. package/src/lib/acp/manager.ts +202 -0
  1334. package/src/lib/acp/registry.ts +384 -0
  1335. package/src/lib/agentSkills/catalog.ts +256 -0
  1336. package/src/lib/agentSkills/cliRegistryParser.ts +242 -0
  1337. package/src/lib/agentSkills/generator.ts +367 -0
  1338. package/src/lib/agentSkills/openapiParser.ts +201 -0
  1339. package/src/lib/agentSkills/schemas.ts +36 -0
  1340. package/src/lib/agentSkills/types.ts +101 -0
  1341. package/src/lib/api/comboErrorResponse.ts +80 -0
  1342. package/src/lib/api/errorResponse.ts +54 -0
  1343. package/src/lib/api/modelTestRunner.ts +390 -0
  1344. package/src/lib/api/proxyRegistryRouteHandlers.ts +142 -0
  1345. package/src/lib/api/requireCliToolsAuth.ts +5 -0
  1346. package/src/lib/api/requireManagementAuth.ts +118 -0
  1347. package/src/lib/api/serverErrorMessage.ts +36 -0
  1348. package/src/lib/apiBridgeServer.ts +233 -0
  1349. package/src/lib/apiKeyExposure.ts +19 -0
  1350. package/src/lib/arenaEloSync.ts +564 -0
  1351. package/src/lib/audit/activityIcons.ts +69 -0
  1352. package/src/lib/audit/highLevelActions.ts +57 -0
  1353. package/src/lib/audit/timeline.ts +132 -0
  1354. package/src/lib/auth/managementPassword.ts +117 -0
  1355. package/src/lib/batches/costEstimator.ts +130 -0
  1356. package/src/lib/batches/csvToJsonl.ts +248 -0
  1357. package/src/lib/batches/dispatch.ts +53 -0
  1358. package/src/lib/batches/retryFailed.ts +77 -0
  1359. package/src/lib/batches/schemas.ts +38 -0
  1360. package/src/lib/batches/types.ts +78 -0
  1361. package/src/lib/batches/validateJsonl.ts +154 -0
  1362. package/src/lib/benchmarks.ts +33 -0
  1363. package/src/lib/build-profile/featureDisabled.ts +23 -0
  1364. package/src/lib/cacheControlSettings.ts +25 -0
  1365. package/src/lib/cacheLayer.ts +220 -0
  1366. package/src/lib/catalog/openrouterCatalog.ts +177 -0
  1367. package/src/lib/cli-helper/claudeProfileAutoSync.ts +92 -0
  1368. package/src/lib/cli-helper/codexProfileAutoSync.ts +80 -0
  1369. package/src/lib/cli-helper/config-generator/claude.ts +25 -0
  1370. package/src/lib/cli-helper/config-generator/cline.ts +23 -0
  1371. package/src/lib/cli-helper/config-generator/codex.ts +34 -0
  1372. package/src/lib/cli-helper/config-generator/continue.ts +37 -0
  1373. package/src/lib/cli-helper/config-generator/hermes-agent.ts +210 -0
  1374. package/src/lib/cli-helper/config-generator/hermes.ts +47 -0
  1375. package/src/lib/cli-helper/config-generator/hermesHome.ts +40 -0
  1376. package/src/lib/cli-helper/config-generator/index.ts +124 -0
  1377. package/src/lib/cli-helper/config-generator/kilocode.ts +23 -0
  1378. package/src/lib/cli-helper/config-generator/opencode.ts +443 -0
  1379. package/src/lib/cli-helper/doctor/checks.ts +41 -0
  1380. package/src/lib/cli-helper/log-streamer.ts +78 -0
  1381. package/src/lib/cli-helper/tool-detector.ts +165 -0
  1382. package/src/lib/cliTools/batchStatusCache.ts +47 -0
  1383. package/src/lib/cliTools/checkToolConfigStatus.ts +112 -0
  1384. package/src/lib/cloudAgent/agents/codex.ts +148 -0
  1385. package/src/lib/cloudAgent/agents/cursor.ts +205 -0
  1386. package/src/lib/cloudAgent/agents/devin.ts +137 -0
  1387. package/src/lib/cloudAgent/agents/jules.ts +354 -0
  1388. package/src/lib/cloudAgent/api.ts +86 -0
  1389. package/src/lib/cloudAgent/baseAgent.ts +98 -0
  1390. package/src/lib/cloudAgent/credentials.ts +89 -0
  1391. package/src/lib/cloudAgent/db.ts +145 -0
  1392. package/src/lib/cloudAgent/index.ts +8 -0
  1393. package/src/lib/cloudAgent/julesApi.ts +7 -0
  1394. package/src/lib/cloudAgent/registry.ts +29 -0
  1395. package/src/lib/cloudAgent/types.ts +90 -0
  1396. package/src/lib/cloudSync.stub.ts +31 -0
  1397. package/src/lib/cloudSync.ts +210 -0
  1398. package/src/lib/cloudflaredTunnel.ts +933 -0
  1399. package/src/lib/combos/autoPromote.ts +92 -0
  1400. package/src/lib/combos/builderDraft.ts +242 -0
  1401. package/src/lib/combos/builderOptions.ts +681 -0
  1402. package/src/lib/combos/compositeTiers.ts +200 -0
  1403. package/src/lib/combos/controlCenter.ts +271 -0
  1404. package/src/lib/combos/intelligentRouting.ts +192 -0
  1405. package/src/lib/combos/steps.ts +322 -0
  1406. package/src/lib/combos/testHealth.ts +190 -0
  1407. package/src/lib/compliance/index.ts +637 -0
  1408. package/src/lib/compliance/noLog.ts +86 -0
  1409. package/src/lib/compliance/providerAudit.ts +98 -0
  1410. package/src/lib/compression/judgeModelClient.ts +43 -0
  1411. package/src/lib/config/hotReload.ts +115 -0
  1412. package/src/lib/config/runtimeSettings.ts +551 -0
  1413. package/src/lib/consoleInterceptor.ts +136 -0
  1414. package/src/lib/container.ts +122 -0
  1415. package/src/lib/contextWindowResolver.ts +147 -0
  1416. package/src/lib/copilot/codegraphKnowledge.ts +229 -0
  1417. package/src/lib/copilot/engine.ts +372 -0
  1418. package/src/lib/copilot/systemPrompt.ts +189 -0
  1419. package/src/lib/copilot/tools.ts +415 -0
  1420. package/src/lib/credentialHealth/cache.ts +206 -0
  1421. package/src/lib/credentialHealth/scheduler.ts +328 -0
  1422. package/src/lib/dataPaths.ts +122 -0
  1423. package/src/lib/db/AGENTS.md +70 -0
  1424. package/src/lib/db/_rowTypes.ts +45 -0
  1425. package/src/lib/db/accessTokens.ts +183 -0
  1426. package/src/lib/db/adapters/betterSqliteAdapter.ts +58 -0
  1427. package/src/lib/db/adapters/driverFactory.ts +98 -0
  1428. package/src/lib/db/adapters/nodeSqliteAdapter.ts +60 -0
  1429. package/src/lib/db/adapters/nodeSqliteShared.ts +148 -0
  1430. package/src/lib/db/adapters/sqljsAdapter.ts +232 -0
  1431. package/src/lib/db/adapters/types.ts +34 -0
  1432. package/src/lib/db/agentBridgeBypass.ts +81 -0
  1433. package/src/lib/db/agentBridgeMappings.ts +47 -0
  1434. package/src/lib/db/agentBridgeState.ts +115 -0
  1435. package/src/lib/db/apiKeyColumnFallbacks.ts +47 -0
  1436. package/src/lib/db/apiKeyContextSources.ts +107 -0
  1437. package/src/lib/db/apiKeyGroups.ts +318 -0
  1438. package/src/lib/db/apiKeyUsageLimitFields.ts +65 -0
  1439. package/src/lib/db/apiKeys/modelPermissions.ts +122 -0
  1440. package/src/lib/db/apiKeys/rowParsers.ts +161 -0
  1441. package/src/lib/db/apiKeys/types.ts +21 -0
  1442. package/src/lib/db/apiKeys.ts +1492 -0
  1443. package/src/lib/db/backup.ts +708 -0
  1444. package/src/lib/db/batches.ts +450 -0
  1445. package/src/lib/db/callLogStats.ts +235 -0
  1446. package/src/lib/db/caseMapping.ts +80 -0
  1447. package/src/lib/db/cleanup.ts +584 -0
  1448. package/src/lib/db/cliToolState.ts +159 -0
  1449. package/src/lib/db/comboForecast.ts +119 -0
  1450. package/src/lib/db/combos.ts +339 -0
  1451. package/src/lib/db/commandCodeAuth.ts +209 -0
  1452. package/src/lib/db/compression.ts +780 -0
  1453. package/src/lib/db/compressionAnalytics.ts +661 -0
  1454. package/src/lib/db/compressionCacheStats.ts +112 -0
  1455. package/src/lib/db/compressionCombos.ts +491 -0
  1456. package/src/lib/db/compressionRunTelemetry.ts +126 -0
  1457. package/src/lib/db/contextHandoffs.ts +233 -0
  1458. package/src/lib/db/core.ts +1520 -0
  1459. package/src/lib/db/creditBalance.ts +92 -0
  1460. package/src/lib/db/databaseSettings.ts +309 -0
  1461. package/src/lib/db/detailedLogs.ts +170 -0
  1462. package/src/lib/db/discovery.ts +97 -0
  1463. package/src/lib/db/discoveryResults.ts +176 -0
  1464. package/src/lib/db/domainState.ts +630 -0
  1465. package/src/lib/db/encryption.ts +327 -0
  1466. package/src/lib/db/evals.ts +786 -0
  1467. package/src/lib/db/featureFlags.ts +82 -0
  1468. package/src/lib/db/files.ts +170 -0
  1469. package/src/lib/db/freeProxies.ts +360 -0
  1470. package/src/lib/db/gamification.ts +613 -0
  1471. package/src/lib/db/healthCheck.ts +565 -0
  1472. package/src/lib/db/inspectorCustomHosts.ts +88 -0
  1473. package/src/lib/db/inspectorSessions.ts +161 -0
  1474. package/src/lib/db/jsonMigration.ts +321 -0
  1475. package/src/lib/db/memoryVec.ts +138 -0
  1476. package/src/lib/db/middleware.ts +239 -0
  1477. package/src/lib/db/migrationRunner/constants.ts +118 -0
  1478. package/src/lib/db/migrationRunner.ts +1069 -0
  1479. package/src/lib/db/migrations/001_initial_schema.sql +226 -0
  1480. package/src/lib/db/migrations/002_mcp_a2a_tables.sql +95 -0
  1481. package/src/lib/db/migrations/003_provider_node_custom_paths.sql +5 -0
  1482. package/src/lib/db/migrations/004_proxy_registry.sql +31 -0
  1483. package/src/lib/db/migrations/005_combo_agent_fields.sql +19 -0
  1484. package/src/lib/db/migrations/006_detailed_request_logs.sql +42 -0
  1485. package/src/lib/db/migrations/007_search_request_type.sql +4 -0
  1486. package/src/lib/db/migrations/008_registered_keys.sql +65 -0
  1487. package/src/lib/db/migrations/009_requested_model.sql +9 -0
  1488. package/src/lib/db/migrations/010_model_combo_mappings.sql +19 -0
  1489. package/src/lib/db/migrations/011_webhooks.sql +15 -0
  1490. package/src/lib/db/migrations/012_fix_token_input_cache_tokens.sql +15 -0
  1491. package/src/lib/db/migrations/013_quota_snapshots.sql +19 -0
  1492. package/src/lib/db/migrations/014_unified_log_artifacts.sql +15 -0
  1493. package/src/lib/db/migrations/015_create_memories.sql +22 -0
  1494. package/src/lib/db/migrations/016_create_skills.sql +37 -0
  1495. package/src/lib/db/migrations/017_version_manager_upstream_proxy.sql +56 -0
  1496. package/src/lib/db/migrations/018_call_logs_detailed_tokens.sql +6 -0
  1497. package/src/lib/db/migrations/019_context_handoffs.sql +28 -0
  1498. package/src/lib/db/migrations/020_combo_sort_order.sql +16 -0
  1499. package/src/lib/db/migrations/021_combo_call_log_targets.sql +5 -0
  1500. package/src/lib/db/migrations/022_add_memory_fts5.sql +33 -0
  1501. package/src/lib/db/migrations/023_fix_memory_fts_uuid.sql +55 -0
  1502. package/src/lib/db/migrations/024_create_sync_tokens.sql +15 -0
  1503. package/src/lib/db/migrations/025_call_logs_summary_storage.sql +145 -0
  1504. package/src/lib/db/migrations/027_skill_mode_and_metadata.sql +10 -0
  1505. package/src/lib/db/migrations/028_create_files_and_batches.sql +51 -0
  1506. package/src/lib/db/migrations/029_provider_connection_max_concurrent.sql +10 -0
  1507. package/src/lib/db/migrations/030_create_eval_runs.sql +28 -0
  1508. package/src/lib/db/migrations/031_create_eval_suites.sql +27 -0
  1509. package/src/lib/db/migrations/032_apikey_lifecycle.sql +24 -0
  1510. package/src/lib/db/migrations/033_create_reasoning_cache.sql +27 -0
  1511. package/src/lib/db/migrations/034_compression_settings.sql +10 -0
  1512. package/src/lib/db/migrations/035_standard_compression_config.sql +9 -0
  1513. package/src/lib/db/migrations/036_aggressive_compression.sql +4 -0
  1514. package/src/lib/db/migrations/037_ultra_compression.sql +6 -0
  1515. package/src/lib/db/migrations/038_compression_analytics.sql +13 -0
  1516. package/src/lib/db/migrations/039_compression_cache_stats.sql +15 -0
  1517. package/src/lib/db/migrations/040_oneproxy_proxy_fields.sql +24 -0
  1518. package/src/lib/db/migrations/041_compression_receipts.sql +18 -0
  1519. package/src/lib/db/migrations/042_compression_combos.sql +43 -0
  1520. package/src/lib/db/migrations/043_default_compression_combo_pipeline.sql +12 -0
  1521. package/src/lib/db/migrations/044_usage_history_api_key_backfill.sql +27 -0
  1522. package/src/lib/db/migrations/045_compression_tokens.sql +4 -0
  1523. package/src/lib/db/migrations/046_database_settings.sql +44 -0
  1524. package/src/lib/db/migrations/047_aggregation_tables.sql +44 -0
  1525. package/src/lib/db/migrations/048_summary_indexes.sql +29 -0
  1526. package/src/lib/db/migrations/049_compression_analytics_indexes.sql +10 -0
  1527. package/src/lib/db/migrations/050_session_account_affinity.sql +11 -0
  1528. package/src/lib/db/migrations/051_hot_path_db_indexes.sql +10 -0
  1529. package/src/lib/db/migrations/052_api_key_combo_throttle.sql +4 -0
  1530. package/src/lib/db/migrations/053_remove_status_from_files.sql +2 -0
  1531. package/src/lib/db/migrations/054_usage_history_service_tier.sql +4 -0
  1532. package/src/lib/db/migrations/056_mcp_accessibility_compression.sql +8 -0
  1533. package/src/lib/db/migrations/057_provider_connection_quota_window_thresholds.sql +14 -0
  1534. package/src/lib/db/migrations/058_command_code_auth_sessions.sql +19 -0
  1535. package/src/lib/db/migrations/059_manifest_routing.sql +21 -0
  1536. package/src/lib/db/migrations/060_create_gamification.sql +94 -0
  1537. package/src/lib/db/migrations/061_cloud_agent_credentials.sql +9 -0
  1538. package/src/lib/db/migrations/062_usage_history_combo_strategy.sql +4 -0
  1539. package/src/lib/db/migrations/063_add_last_model_to_handoffs.sql +7 -0
  1540. package/src/lib/db/migrations/064_create_session_model_history.sql +19 -0
  1541. package/src/lib/db/migrations/065_middleware_hooks.sql +45 -0
  1542. package/src/lib/db/migrations/066_api_key_groups.sql +47 -0
  1543. package/src/lib/db/migrations/067_relay_proxies.sql +56 -0
  1544. package/src/lib/db/migrations/068_free_proxies.sql +52 -0
  1545. package/src/lib/db/migrations/069_webhook_deliveries.sql +15 -0
  1546. package/src/lib/db/migrations/070_webhooks_kind_metadata.sql +3 -0
  1547. package/src/lib/db/migrations/071_services.sql +19 -0
  1548. package/src/lib/db/migrations/072_free_proxies_fk.sql +27 -0
  1549. package/src/lib/db/migrations/073_per_model_token_limits.sql +61 -0
  1550. package/src/lib/db/migrations/074_discovery_results.sql +20 -0
  1551. package/src/lib/db/migrations/075_api_key_self_service_usage_scopes.sql +51 -0
  1552. package/src/lib/db/migrations/076_create_plugins.sql +31 -0
  1553. package/src/lib/db/migrations/077_api_key_stream_default_mode.sql +3 -0
  1554. package/src/lib/db/migrations/078_quota_consumption.sql +24 -0
  1555. package/src/lib/db/migrations/079_provider_plans.sql +19 -0
  1556. package/src/lib/db/migrations/080_agent_bridge.sql +24 -0
  1557. package/src/lib/db/migrations/081_inspector_custom_hosts.sql +11 -0
  1558. package/src/lib/db/migrations/082_inspector_sessions.sql +18 -0
  1559. package/src/lib/db/migrations/083_memory_vec.sql +26 -0
  1560. package/src/lib/db/migrations/084_playground_presets.sql +15 -0
  1561. package/src/lib/db/migrations/085_quota_pools.sql +39 -0
  1562. package/src/lib/db/migrations/086_api_key_allowed_quotas.sql +8 -0
  1563. package/src/lib/db/migrations/087_quota_pool_connections.sql +21 -0
  1564. package/src/lib/db/migrations/088_quota_groups.sql +28 -0
  1565. package/src/lib/db/migrations/089_add_disable_non_public_to_api_keys.sql +3 -0
  1566. package/src/lib/db/migrations/090_plugin_metrics.sql +9 -0
  1567. package/src/lib/db/migrations/091_plugin_analytics.sql +23 -0
  1568. package/src/lib/db/migrations/092_api_key_context_sources.sql +13 -0
  1569. package/src/lib/db/migrations/093_proxy_enable_toggles.sql +17 -0
  1570. package/src/lib/db/migrations/094_per_key_proxy_toggles.sql +12 -0
  1571. package/src/lib/db/migrations/095_provider_node_custom_headers.sql +5 -0
  1572. package/src/lib/db/migrations/096_sync_context_cache_protection.sql +9 -0
  1573. package/src/lib/db/migrations/097_model_intelligence.sql +25 -0
  1574. package/src/lib/db/migrations/098_clear_semantic_cache_for_key_isolation.sql +4 -0
  1575. package/src/lib/db/migrations/099_proxy_family.sql +5 -0
  1576. package/src/lib/db/migrations/100_cli_access_tokens.sql +18 -0
  1577. package/src/lib/db/migrations/101_api_key_usage_limits.sql +4 -0
  1578. package/src/lib/db/migrations/102_compression_engines_map.sql +5 -0
  1579. package/src/lib/db/migrations/103_strip_legacy_combo_config_keys.sql +54 -0
  1580. package/src/lib/db/migrations/104_normalize_database_cache_size.sql +10 -0
  1581. package/src/lib/db/migrations/105_usage_history_endpoint.sql +6 -0
  1582. package/src/lib/db/migrations/106_quota_allocation_model_caps.sql +32 -0
  1583. package/src/lib/db/migrations/107_quota_combos_quota_share_strategy.sql +25 -0
  1584. package/src/lib/db/migrations/108_provider_quota_reset_events.sql +26 -0
  1585. package/src/lib/db/migrations/109_call_logs_correlation_id.sql +2 -0
  1586. package/src/lib/db/migrations/110_model_context_overrides.sql +17 -0
  1587. package/src/lib/db/migrations/111_memory_typed_decay.sql +10 -0
  1588. package/src/lib/db/migrations/112_batch_item_checkpoints.sql +18 -0
  1589. package/src/lib/db/migrations/113_provider_node_icon_url.sql +5 -0
  1590. package/src/lib/db/migrations/114_mux_service_seed.sql +12 -0
  1591. package/src/lib/db/migrations/115_bifrost_service.sql +9 -0
  1592. package/src/lib/db/migrations/116_call_logs_reasoning_source.sql +13 -0
  1593. package/src/lib/db/migrations/117_proxy_pool_rotation.sql +53 -0
  1594. package/src/lib/db/modelComboMappings.ts +255 -0
  1595. package/src/lib/db/modelContextOverrides.ts +133 -0
  1596. package/src/lib/db/modelIntelligence.ts +232 -0
  1597. package/src/lib/db/models/aliases.ts +61 -0
  1598. package/src/lib/db/models/compat.ts +249 -0
  1599. package/src/lib/db/models/mitmAlias.ts +32 -0
  1600. package/src/lib/db/models/shared.ts +19 -0
  1601. package/src/lib/db/models.ts +936 -0
  1602. package/src/lib/db/notion.ts +48 -0
  1603. package/src/lib/db/obsidian.ts +283 -0
  1604. package/src/lib/db/oneproxy.ts +293 -0
  1605. package/src/lib/db/optimizationSettings.ts +284 -0
  1606. package/src/lib/db/playgroundPresets.ts +167 -0
  1607. package/src/lib/db/pluginMetrics.ts +88 -0
  1608. package/src/lib/db/plugins.ts +280 -0
  1609. package/src/lib/db/prompts.ts +302 -0
  1610. package/src/lib/db/providerLimits.ts +131 -0
  1611. package/src/lib/db/providerNodeSelect.ts +48 -0
  1612. package/src/lib/db/providerPlans.ts +149 -0
  1613. package/src/lib/db/providerStats.ts +68 -0
  1614. package/src/lib/db/providers/columns.ts +116 -0
  1615. package/src/lib/db/providers/nodes.ts +169 -0
  1616. package/src/lib/db/providers/rateLimit.ts +198 -0
  1617. package/src/lib/db/providers.ts +803 -0
  1618. package/src/lib/db/proxies/mappers.ts +181 -0
  1619. package/src/lib/db/proxies/types.ts +78 -0
  1620. package/src/lib/db/proxies.ts +1176 -0
  1621. package/src/lib/db/proxyLogs.ts +35 -0
  1622. package/src/lib/db/quotaConsumption.ts +274 -0
  1623. package/src/lib/db/quotaGroups.ts +147 -0
  1624. package/src/lib/db/quotaModelCaps.ts +128 -0
  1625. package/src/lib/db/quotaPools.ts +548 -0
  1626. package/src/lib/db/quotaResetEvents.ts +334 -0
  1627. package/src/lib/db/quotaSnapshots.ts +209 -0
  1628. package/src/lib/db/readCache.ts +202 -0
  1629. package/src/lib/db/reasoningCache.ts +276 -0
  1630. package/src/lib/db/recovery.ts +33 -0
  1631. package/src/lib/db/registeredKeys.ts +532 -0
  1632. package/src/lib/db/relayProxies.ts +364 -0
  1633. package/src/lib/db/schemaColumns.ts +226 -0
  1634. package/src/lib/db/secrets.ts +28 -0
  1635. package/src/lib/db/semanticCache.ts +117 -0
  1636. package/src/lib/db/serviceModels.ts +79 -0
  1637. package/src/lib/db/sessionAccountAffinity.ts +212 -0
  1638. package/src/lib/db/settings/cacheMetrics.ts +235 -0
  1639. package/src/lib/db/settings/lkgp.ts +49 -0
  1640. package/src/lib/db/settings/pricing.ts +254 -0
  1641. package/src/lib/db/settings/shared.ts +9 -0
  1642. package/src/lib/db/settings.ts +661 -0
  1643. package/src/lib/db/skills.ts +62 -0
  1644. package/src/lib/db/stateReset.ts +30 -0
  1645. package/src/lib/db/stats.ts +71 -0
  1646. package/src/lib/db/syncTokens.ts +163 -0
  1647. package/src/lib/db/tierConfig.ts +88 -0
  1648. package/src/lib/db/tokenLimits.ts +295 -0
  1649. package/src/lib/db/upstreamProxy.ts +247 -0
  1650. package/src/lib/db/usageAnalytics/sources.ts +208 -0
  1651. package/src/lib/db/usageAnalytics.ts +723 -0
  1652. package/src/lib/db/usageLogs.ts +94 -0
  1653. package/src/lib/db/usageSummary.ts +18 -0
  1654. package/src/lib/db/vacuumScheduler.ts +326 -0
  1655. package/src/lib/db/versionManager.ts +377 -0
  1656. package/src/lib/db/webSessionDedup.ts +57 -0
  1657. package/src/lib/db/webhookDeliveries.ts +77 -0
  1658. package/src/lib/db/webhooks.ts +178 -0
  1659. package/src/lib/discovery/index.ts +137 -0
  1660. package/src/lib/display/names.ts +73 -0
  1661. package/src/lib/display/useProviderNodeMap.ts +68 -0
  1662. package/src/lib/docsI18nPath.ts +33 -0
  1663. package/src/lib/docsSanitizer.ts +42 -0
  1664. package/src/lib/embeddings/familyGuard.ts +24 -0
  1665. package/src/lib/embeddings/service.ts +288 -0
  1666. package/src/lib/env/runtimeEnv.ts +166 -0
  1667. package/src/lib/evals/evalRunner/builtinSuites.ts +676 -0
  1668. package/src/lib/evals/evalRunner.ts +301 -0
  1669. package/src/lib/evals/runtime.ts +311 -0
  1670. package/src/lib/events/eventBus.ts +148 -0
  1671. package/src/lib/events/types.ts +181 -0
  1672. package/src/lib/freeProviderRankings.ts +340 -0
  1673. package/src/lib/freeProxyProviders/index.ts +24 -0
  1674. package/src/lib/freeProxyProviders/iplocate.ts +107 -0
  1675. package/src/lib/freeProxyProviders/oneproxy.ts +127 -0
  1676. package/src/lib/freeProxyProviders/proxifly.ts +143 -0
  1677. package/src/lib/freeProxyProviders/types.ts +33 -0
  1678. package/src/lib/freeProxyProviders/webshare.ts +148 -0
  1679. package/src/lib/gamification/antiCheat.ts +160 -0
  1680. package/src/lib/gamification/badges.ts +542 -0
  1681. package/src/lib/gamification/events.ts +187 -0
  1682. package/src/lib/gamification/index.ts +37 -0
  1683. package/src/lib/gamification/invites.ts +127 -0
  1684. package/src/lib/gamification/leaderboard.ts +64 -0
  1685. package/src/lib/gamification/notifications.ts +118 -0
  1686. package/src/lib/gamification/servers.ts +179 -0
  1687. package/src/lib/gamification/sharing.ts +56 -0
  1688. package/src/lib/gamification/streaks.ts +164 -0
  1689. package/src/lib/gamification/xp.ts +152 -0
  1690. package/src/lib/gracefulShutdown.ts +151 -0
  1691. package/src/lib/guardrails/base.ts +65 -0
  1692. package/src/lib/guardrails/index.ts +4 -0
  1693. package/src/lib/guardrails/piiMasker.ts +208 -0
  1694. package/src/lib/guardrails/promptInjection.ts +285 -0
  1695. package/src/lib/guardrails/registry.ts +282 -0
  1696. package/src/lib/guardrails/visionBridge.ts +237 -0
  1697. package/src/lib/guardrails/visionBridgeHelpers.ts +499 -0
  1698. package/src/lib/guardrails/visionBridgeRouter.ts +271 -0
  1699. package/src/lib/headroom/detect.ts +214 -0
  1700. package/src/lib/headroom/process.ts +177 -0
  1701. package/src/lib/idempotencyLayer.ts +105 -0
  1702. package/src/lib/images/imageRouteModel.ts +168 -0
  1703. package/src/lib/initCloudSync.ts +52 -0
  1704. package/src/lib/inspector/agentBridgeMaintenanceApi.ts +70 -0
  1705. package/src/lib/inspector/captureState.ts +90 -0
  1706. package/src/lib/inspector/configPortability.ts +86 -0
  1707. package/src/lib/inspector/harExport.ts +188 -0
  1708. package/src/lib/inspector/matchesTrafficFilter.ts +37 -0
  1709. package/src/lib/inspector/secretMask.ts +6 -0
  1710. package/src/lib/inspector/tproxyCaptureApi.ts +60 -0
  1711. package/src/lib/ipUtils.ts +94 -0
  1712. package/src/lib/jobs/budgetResetJob.ts +40 -0
  1713. package/src/lib/jobs/reasoningCacheCleanupJob.ts +40 -0
  1714. package/src/lib/localDb.ts +797 -0
  1715. package/src/lib/localHealthCheck.ts +267 -0
  1716. package/src/lib/logEnv.ts +164 -0
  1717. package/src/lib/logPayloads.ts +110 -0
  1718. package/src/lib/logRotation.ts +206 -0
  1719. package/src/lib/machineToken.ts +53 -0
  1720. package/src/lib/memory/cache.ts +76 -0
  1721. package/src/lib/memory/embedding/cache.ts +77 -0
  1722. package/src/lib/memory/embedding/index.ts +281 -0
  1723. package/src/lib/memory/embedding/remote.ts +95 -0
  1724. package/src/lib/memory/embedding/staticPotion.ts +253 -0
  1725. package/src/lib/memory/embedding/transformersLocal.ts +153 -0
  1726. package/src/lib/memory/embedding/types.ts +40 -0
  1727. package/src/lib/memory/extraction.ts +195 -0
  1728. package/src/lib/memory/injection.ts +211 -0
  1729. package/src/lib/memory/qdrant.ts +521 -0
  1730. package/src/lib/memory/reindex.ts +102 -0
  1731. package/src/lib/memory/retrieval/scoring.ts +104 -0
  1732. package/src/lib/memory/retrieval.ts +1072 -0
  1733. package/src/lib/memory/schemas.ts +39 -0
  1734. package/src/lib/memory/settings.ts +184 -0
  1735. package/src/lib/memory/store.ts +621 -0
  1736. package/src/lib/memory/summarization.ts +202 -0
  1737. package/src/lib/memory/typedDecay.ts +245 -0
  1738. package/src/lib/memory/types.ts +45 -0
  1739. package/src/lib/memory/vectorStore.ts +425 -0
  1740. package/src/lib/memory/verify.ts +56 -0
  1741. package/src/lib/middleware/cliTokenAuth.ts +91 -0
  1742. package/src/lib/middleware/registry.ts +432 -0
  1743. package/src/lib/middleware/types.ts +129 -0
  1744. package/src/lib/modelAliasSeed.ts +82 -0
  1745. package/src/lib/modelCapabilities.ts +481 -0
  1746. package/src/lib/modelMetadataRegistry.ts +574 -0
  1747. package/src/lib/modelsDevSync/transform.ts +279 -0
  1748. package/src/lib/modelsDevSync.ts +677 -0
  1749. package/src/lib/monitoring/comboHealthAutopilot.ts +494 -0
  1750. package/src/lib/monitoring/observability.ts +336 -0
  1751. package/src/lib/monitoring/providerHealthAutopilot.ts +743 -0
  1752. package/src/lib/monitoring/providerHealthMatrix.ts +621 -0
  1753. package/src/lib/ngrokTunnel.ts +142 -0
  1754. package/src/lib/notion/api.ts +247 -0
  1755. package/src/lib/oauth/codexDeviceFlow.ts +316 -0
  1756. package/src/lib/oauth/config/index.ts +31 -0
  1757. package/src/lib/oauth/connectionPersistence.ts +114 -0
  1758. package/src/lib/oauth/constants/oauth.ts +494 -0
  1759. package/src/lib/oauth/credentialBlob.ts +105 -0
  1760. package/src/lib/oauth/deviceFlowTickets.ts +110 -0
  1761. package/src/lib/oauth/gitlab.ts +103 -0
  1762. package/src/lib/oauth/pasteCredentials.ts +51 -0
  1763. package/src/lib/oauth/providers/agy.ts +15 -0
  1764. package/src/lib/oauth/providers/antigravity.ts +159 -0
  1765. package/src/lib/oauth/providers/claude.ts +170 -0
  1766. package/src/lib/oauth/providers/cline.ts +91 -0
  1767. package/src/lib/oauth/providers/codebuddy-cn.ts +123 -0
  1768. package/src/lib/oauth/providers/codex.ts +214 -0
  1769. package/src/lib/oauth/providers/cursor.ts +15 -0
  1770. package/src/lib/oauth/providers/github.ts +89 -0
  1771. package/src/lib/oauth/providers/gitlab-duo.ts +124 -0
  1772. package/src/lib/oauth/providers/grok-cli.ts +155 -0
  1773. package/src/lib/oauth/providers/index.ts +59 -0
  1774. package/src/lib/oauth/providers/kilocode.ts +77 -0
  1775. package/src/lib/oauth/providers/kimi-coding.ts +135 -0
  1776. package/src/lib/oauth/providers/kiro.ts +185 -0
  1777. package/src/lib/oauth/providers/qoder.ts +81 -0
  1778. package/src/lib/oauth/providers/qwen.ts +82 -0
  1779. package/src/lib/oauth/providers/trae.ts +78 -0
  1780. package/src/lib/oauth/providers/windsurf.ts +64 -0
  1781. package/src/lib/oauth/providers/zed-hosted.ts +89 -0
  1782. package/src/lib/oauth/providers/zed.ts +35 -0
  1783. package/src/lib/oauth/providers.ts +278 -0
  1784. package/src/lib/oauth/services/codexImport.ts +222 -0
  1785. package/src/lib/oauth/services/cursor.ts +219 -0
  1786. package/src/lib/oauth/services/kiro.ts +441 -0
  1787. package/src/lib/oauth/utils/agyAuthImport.ts +259 -0
  1788. package/src/lib/oauth/utils/banner.ts +62 -0
  1789. package/src/lib/oauth/utils/claudeAuthFile.ts +334 -0
  1790. package/src/lib/oauth/utils/claudeAuthImport.ts +259 -0
  1791. package/src/lib/oauth/utils/claudeAuthZipExtract.ts +5 -0
  1792. package/src/lib/oauth/utils/cliProxyAuthImport.ts +142 -0
  1793. package/src/lib/oauth/utils/codexAuthFile.ts +352 -0
  1794. package/src/lib/oauth/utils/codexAuthImport.ts +294 -0
  1795. package/src/lib/oauth/utils/codexAuthZipExtract.ts +5 -0
  1796. package/src/lib/oauth/utils/jsonZipExtract.ts +89 -0
  1797. package/src/lib/oauth/utils/pkce.ts +37 -0
  1798. package/src/lib/oauth/utils/server.ts +122 -0
  1799. package/src/lib/oauth/utils/ui.ts +47 -0
  1800. package/src/lib/obsidian/api.ts +390 -0
  1801. package/src/lib/obsidianSync.ts +110 -0
  1802. package/src/lib/oneproxyRotator.ts +19 -0
  1803. package/src/lib/oneproxySync.ts +182 -0
  1804. package/src/lib/piiSanitizer.ts +416 -0
  1805. package/src/lib/playground/codeExport.ts +408 -0
  1806. package/src/lib/playground/promptImprover.ts +147 -0
  1807. package/src/lib/playground/streamMetrics.ts +61 -0
  1808. package/src/lib/playground/types.ts +114 -0
  1809. package/src/lib/plugins/devMode.ts +65 -0
  1810. package/src/lib/plugins/doctor.ts +138 -0
  1811. package/src/lib/plugins/errors.ts +38 -0
  1812. package/src/lib/plugins/hooks.ts +322 -0
  1813. package/src/lib/plugins/index.ts +72 -0
  1814. package/src/lib/plugins/loader.ts +326 -0
  1815. package/src/lib/plugins/logger.ts +50 -0
  1816. package/src/lib/plugins/manager.ts +579 -0
  1817. package/src/lib/plugins/manifest.ts +213 -0
  1818. package/src/lib/plugins/marketplace.ts +202 -0
  1819. package/src/lib/plugins/pluginWorker.ts +246 -0
  1820. package/src/lib/plugins/sandbox.ts +29 -0
  1821. package/src/lib/plugins/scanner.ts +110 -0
  1822. package/src/lib/plugins/sdk.ts +88 -0
  1823. package/src/lib/plugins/signing.ts +34 -0
  1824. package/src/lib/plugins/testRunner.ts +95 -0
  1825. package/src/lib/plugins/watcher.ts +90 -0
  1826. package/src/lib/pricingSync.ts +456 -0
  1827. package/src/lib/promptCache/index.ts +1 -0
  1828. package/src/lib/promptCache/prefixAnalyzer.ts +81 -0
  1829. package/src/lib/providerModels/cursorAgent.ts +166 -0
  1830. package/src/lib/providerModels/geminiModelsParser.ts +91 -0
  1831. package/src/lib/providerModels/managedAvailableModels.ts +165 -0
  1832. package/src/lib/providerModels/managedModelImport.ts +349 -0
  1833. package/src/lib/providerModels/modelDiscovery.ts +151 -0
  1834. package/src/lib/providers/catalog.ts +246 -0
  1835. package/src/lib/providers/claudeExtraUsage.ts +160 -0
  1836. package/src/lib/providers/claudeFastMode.ts +75 -0
  1837. package/src/lib/providers/codexConnectionDefaults.ts +85 -0
  1838. package/src/lib/providers/codexFastTier.ts +210 -0
  1839. package/src/lib/providers/imageValidation.ts +160 -0
  1840. package/src/lib/providers/managedAvailableModels.ts +21 -0
  1841. package/src/lib/providers/modelListingCapability.ts +26 -0
  1842. package/src/lib/providers/nvidiaValidationModel.ts +24 -0
  1843. package/src/lib/providers/requestDefaults.ts +318 -0
  1844. package/src/lib/providers/serviceKindIndex.ts +0 -0
  1845. package/src/lib/providers/staticModels.ts +170 -0
  1846. package/src/lib/providers/validation/anthropicFormat.ts +319 -0
  1847. package/src/lib/providers/validation/audioMiscProviders.ts +582 -0
  1848. package/src/lib/providers/validation/cloudProviders.ts +669 -0
  1849. package/src/lib/providers/validation/cozeError.ts +56 -0
  1850. package/src/lib/providers/validation/directChatProbe.ts +47 -0
  1851. package/src/lib/providers/validation/embeddingProviders.ts +143 -0
  1852. package/src/lib/providers/validation/headers.ts +122 -0
  1853. package/src/lib/providers/validation/metaAi.ts +117 -0
  1854. package/src/lib/providers/validation/openaiFormat.ts +500 -0
  1855. package/src/lib/providers/validation/searchProviders.ts +199 -0
  1856. package/src/lib/providers/validation/transport.ts +109 -0
  1857. package/src/lib/providers/validation/urlHelpers.ts +114 -0
  1858. package/src/lib/providers/validation/webProvidersA.ts +808 -0
  1859. package/src/lib/providers/validation/webProvidersB.ts +536 -0
  1860. package/src/lib/providers/validation.ts +772 -0
  1861. package/src/lib/providers/webCookieAuth.ts +157 -0
  1862. package/src/lib/providers/xai/thinking.ts +127 -0
  1863. package/src/lib/providers/xai/translators/claude.ts +368 -0
  1864. package/src/lib/providers/xai/translators/gemini.ts +371 -0
  1865. package/src/lib/providers/xai/translators/openai-chat.ts +310 -0
  1866. package/src/lib/providers/xai/translators/openai-responses.ts +77 -0
  1867. package/src/lib/proxyEgress.ts +388 -0
  1868. package/src/lib/proxyHealth/decision.ts +77 -0
  1869. package/src/lib/proxyHealth/scheduler.ts +207 -0
  1870. package/src/lib/proxyHealth/statusPolicy.ts +41 -0
  1871. package/src/lib/proxyHealth.ts +173 -0
  1872. package/src/lib/proxyLogger.ts +240 -0
  1873. package/src/lib/proxyRelay/cloudflareWorkerScript.ts +107 -0
  1874. package/src/lib/quota/QuotaStore.ts +23 -0
  1875. package/src/lib/quota/accountBuckets.ts +225 -0
  1876. package/src/lib/quota/burnRate.ts +111 -0
  1877. package/src/lib/quota/connectionProvider.ts +32 -0
  1878. package/src/lib/quota/connectionRecovery.ts +303 -0
  1879. package/src/lib/quota/dimensions.ts +61 -0
  1880. package/src/lib/quota/enforce.ts +401 -0
  1881. package/src/lib/quota/fairShare.ts +186 -0
  1882. package/src/lib/quota/planRegistry.ts +107 -0
  1883. package/src/lib/quota/planResolver.ts +78 -0
  1884. package/src/lib/quota/quotaCombos.ts +414 -0
  1885. package/src/lib/quota/quotaKey.ts +202 -0
  1886. package/src/lib/quota/quotaModelNaming.ts +84 -0
  1887. package/src/lib/quota/redisQuotaStore.ts +352 -0
  1888. package/src/lib/quota/saturationSignals.ts +559 -0
  1889. package/src/lib/quota/spendRecorder.ts +131 -0
  1890. package/src/lib/quota/sqliteQuotaStore.ts +282 -0
  1891. package/src/lib/quota/storeFactory.ts +124 -0
  1892. package/src/lib/quota/types.ts +94 -0
  1893. package/src/lib/resilience/modelLockoutSettings.ts +95 -0
  1894. package/src/lib/resilience/settings/normalize.ts +359 -0
  1895. package/src/lib/resilience/settings/types.ts +182 -0
  1896. package/src/lib/resilience/settings.ts +386 -0
  1897. package/src/lib/runtime/ports.ts +32 -0
  1898. package/src/lib/search/executeWebSearch.ts +270 -0
  1899. package/src/lib/security/localEndpoints.ts +82 -0
  1900. package/src/lib/semanticCache.ts +377 -0
  1901. package/src/lib/services/ServiceSupervisor.ts +243 -0
  1902. package/src/lib/services/apiKey.ts +40 -0
  1903. package/src/lib/services/bootstrap.ts +133 -0
  1904. package/src/lib/services/embedPath.ts +19 -0
  1905. package/src/lib/services/embedWsProxy.ts +285 -0
  1906. package/src/lib/services/healthCheck.ts +79 -0
  1907. package/src/lib/services/htmlRewriter.ts +185 -0
  1908. package/src/lib/services/installers/bifrost.ts +145 -0
  1909. package/src/lib/services/installers/cliproxy.ts +116 -0
  1910. package/src/lib/services/installers/mux.ts +178 -0
  1911. package/src/lib/services/installers/ninerouter.stub.ts +17 -0
  1912. package/src/lib/services/installers/ninerouter.ts +157 -0
  1913. package/src/lib/services/installers/utils.ts +167 -0
  1914. package/src/lib/services/modelSync.ts +105 -0
  1915. package/src/lib/services/portProbe.ts +104 -0
  1916. package/src/lib/services/registry.ts +42 -0
  1917. package/src/lib/services/reverseProxy.ts +180 -0
  1918. package/src/lib/services/ringBuffer.ts +91 -0
  1919. package/src/lib/services/types.ts +50 -0
  1920. package/src/lib/settingsCache.ts +62 -0
  1921. package/src/lib/skills/a2a.ts +34 -0
  1922. package/src/lib/skills/builtin/browser.ts +22 -0
  1923. package/src/lib/skills/builtins.ts +493 -0
  1924. package/src/lib/skills/custom.ts +41 -0
  1925. package/src/lib/skills/executor.ts +196 -0
  1926. package/src/lib/skills/githubCollector.ts +426 -0
  1927. package/src/lib/skills/hybrid.ts +67 -0
  1928. package/src/lib/skills/injection.ts +307 -0
  1929. package/src/lib/skills/interception.ts +343 -0
  1930. package/src/lib/skills/providerSettings.ts +14 -0
  1931. package/src/lib/skills/registry.ts +398 -0
  1932. package/src/lib/skills/sandbox.ts +178 -0
  1933. package/src/lib/skills/schemas.ts +43 -0
  1934. package/src/lib/skills/skillssh.ts +73 -0
  1935. package/src/lib/skills/types.ts +61 -0
  1936. package/src/lib/source.ts +7 -0
  1937. package/src/lib/spend/batchWriter.ts +208 -0
  1938. package/src/lib/sseTextTransform.ts +346 -0
  1939. package/src/lib/streamingPiiTransform.ts +350 -0
  1940. package/src/lib/sync/bundle.ts +208 -0
  1941. package/src/lib/sync/tokens.ts +107 -0
  1942. package/src/lib/system/autoUpdate.ts +400 -0
  1943. package/src/lib/system/globalPackagePath.ts +49 -0
  1944. package/src/lib/system/versionCheck.ts +163 -0
  1945. package/src/lib/tailscaleTunnel.ts +1201 -0
  1946. package/src/lib/tokenHealthCheck.ts +829 -0
  1947. package/src/lib/toolPolicy.ts +150 -0
  1948. package/src/lib/translator/streamTransform.ts +27 -0
  1949. package/src/lib/translatorEvents.ts +40 -0
  1950. package/src/lib/usage/aggregateHistory.ts +212 -0
  1951. package/src/lib/usage/apiKeySelfService.ts +420 -0
  1952. package/src/lib/usage/apiKeyUsageLimits.ts +578 -0
  1953. package/src/lib/usage/callLogArtifacts.ts +337 -0
  1954. package/src/lib/usage/callLogs/format.ts +129 -0
  1955. package/src/lib/usage/callLogs.ts +928 -0
  1956. package/src/lib/usage/callLogsBoundedQueries.ts +42 -0
  1957. package/src/lib/usage/codexResetCredits.ts +355 -0
  1958. package/src/lib/usage/comboForecast.ts +441 -0
  1959. package/src/lib/usage/comboHealth.ts +545 -0
  1960. package/src/lib/usage/comboHealthDashboard.ts +109 -0
  1961. package/src/lib/usage/comboScoringInspector.ts +416 -0
  1962. package/src/lib/usage/completedRequestDetails.ts +103 -0
  1963. package/src/lib/usage/costCalculator.ts +279 -0
  1964. package/src/lib/usage/fetcher.ts +483 -0
  1965. package/src/lib/usage/flatRateProviders.ts +59 -0
  1966. package/src/lib/usage/internalUsageCommand.ts +788 -0
  1967. package/src/lib/usage/migrations.ts +471 -0
  1968. package/src/lib/usage/pendingRequestScope.ts +26 -0
  1969. package/src/lib/usage/providerLimits/quotaNormalize.ts +72 -0
  1970. package/src/lib/usage/providerLimits.ts +997 -0
  1971. package/src/lib/usage/providerWindowCosts.ts +571 -0
  1972. package/src/lib/usage/resilienceExplain.ts +468 -0
  1973. package/src/lib/usage/routeExplain.ts +747 -0
  1974. package/src/lib/usage/tokenAccounting.ts +212 -0
  1975. package/src/lib/usage/usageEvents.ts +91 -0
  1976. package/src/lib/usage/usageHistory/helpers.ts +85 -0
  1977. package/src/lib/usage/usageHistory.ts +916 -0
  1978. package/src/lib/usage/usageStats.ts +581 -0
  1979. package/src/lib/usageAnalytics.ts +372 -0
  1980. package/src/lib/usageDb.ts +38 -0
  1981. package/src/lib/versionManager/binaryManager.ts +240 -0
  1982. package/src/lib/versionManager/healthMonitor.ts +71 -0
  1983. package/src/lib/versionManager/index.ts +155 -0
  1984. package/src/lib/versionManager/processManager.ts +155 -0
  1985. package/src/lib/versionManager/releaseChecker.ts +98 -0
  1986. package/src/lib/vscode/familyFirstModelIds.ts +92 -0
  1987. package/src/lib/vscode/modelPresentation.ts +132 -0
  1988. package/src/lib/vscode/reasoningMetadata.ts +183 -0
  1989. package/src/lib/vscode/serviceTierVariants.ts +202 -0
  1990. package/src/lib/vscode/tokenizedRequest.ts +79 -0
  1991. package/src/lib/webhookDispatcher.ts +221 -0
  1992. package/src/lib/webhooks/eventDescriptions.ts +76 -0
  1993. package/src/lib/webhooks/integrations/discord.ts +49 -0
  1994. package/src/lib/webhooks/integrations/slack.ts +45 -0
  1995. package/src/lib/webhooks/integrations/telegram.ts +67 -0
  1996. package/src/lib/ws/handshake.ts +129 -0
  1997. package/src/lib/zed-oauth/confirmedAccounts.ts +38 -0
  1998. package/src/lib/zed-oauth/credentialFingerprint.ts +23 -0
  1999. package/src/lib/zed-oauth/dockerDetect.ts +38 -0
  2000. package/src/lib/zed-oauth/importUtils.ts +46 -0
  2001. package/src/lib/zed-oauth/keychain-reader.stub.ts +28 -0
  2002. package/src/lib/zed-oauth/keychain-reader.ts +231 -0
  2003. package/src/mitm/_internal/bypass.cjs +170 -0
  2004. package/src/mitm/_internal/forwardTarget.cjs +55 -0
  2005. package/src/mitm/_internal/ingest.cjs +82 -0
  2006. package/src/mitm/cert/generate.ts +41 -0
  2007. package/src/mitm/cert/install.stub.ts +23 -0
  2008. package/src/mitm/cert/install.ts +440 -0
  2009. package/src/mitm/dataDir.ts +41 -0
  2010. package/src/mitm/detection/antigravity.ts +33 -0
  2011. package/src/mitm/detection/claudeCode.ts +25 -0
  2012. package/src/mitm/detection/codex.ts +25 -0
  2013. package/src/mitm/detection/copilot.ts +39 -0
  2014. package/src/mitm/detection/cursor.ts +31 -0
  2015. package/src/mitm/detection/index.ts +53 -0
  2016. package/src/mitm/detection/kiro.ts +30 -0
  2017. package/src/mitm/detection/openCode.ts +32 -0
  2018. package/src/mitm/detection/zed.ts +26 -0
  2019. package/src/mitm/dns/dnsConfig.ts +242 -0
  2020. package/src/mitm/dns/provision.ts +92 -0
  2021. package/src/mitm/handlers/antigravity.ts +192 -0
  2022. package/src/mitm/handlers/base.ts +333 -0
  2023. package/src/mitm/handlers/claudeCode.ts +56 -0
  2024. package/src/mitm/handlers/codex.ts +55 -0
  2025. package/src/mitm/handlers/copilot.ts +55 -0
  2026. package/src/mitm/handlers/cursor.ts +55 -0
  2027. package/src/mitm/handlers/kiro.ts +58 -0
  2028. package/src/mitm/handlers/openCode.ts +55 -0
  2029. package/src/mitm/handlers/trae.ts +24 -0
  2030. package/src/mitm/handlers/zed.ts +55 -0
  2031. package/src/mitm/inspector/agentBridgeHook.ts +137 -0
  2032. package/src/mitm/inspector/buffer.ts +201 -0
  2033. package/src/mitm/inspector/contextKey.ts +98 -0
  2034. package/src/mitm/inspector/conversationNormalizer.ts +393 -0
  2035. package/src/mitm/inspector/diagnostics.ts +67 -0
  2036. package/src/mitm/inspector/httpProxyServer.ts +272 -0
  2037. package/src/mitm/inspector/kindDetector.ts +87 -0
  2038. package/src/mitm/inspector/llmMetadataExtractor.ts +183 -0
  2039. package/src/mitm/inspector/pricing.ts +57 -0
  2040. package/src/mitm/inspector/processAttribution.ts +107 -0
  2041. package/src/mitm/inspector/sseMerger.ts +316 -0
  2042. package/src/mitm/inspector/systemProxyConfig.ts +316 -0
  2043. package/src/mitm/inspector/types.ts +112 -0
  2044. package/src/mitm/manager.runtime.ts +5 -0
  2045. package/src/mitm/manager.stub.ts +44 -0
  2046. package/src/mitm/manager.ts +762 -0
  2047. package/src/mitm/maskSecrets.ts +31 -0
  2048. package/src/mitm/passthrough.ts +76 -0
  2049. package/src/mitm/sanitizeHeaders.ts +61 -0
  2050. package/src/mitm/server.cjs +786 -0
  2051. package/src/mitm/socketTimeouts.ts +24 -0
  2052. package/src/mitm/systemCommands.ts +280 -0
  2053. package/src/mitm/targets/antigravity.ts +85 -0
  2054. package/src/mitm/targets/claudeCode.ts +37 -0
  2055. package/src/mitm/targets/codex.ts +30 -0
  2056. package/src/mitm/targets/copilot.ts +32 -0
  2057. package/src/mitm/targets/cursor.ts +33 -0
  2058. package/src/mitm/targets/index.ts +75 -0
  2059. package/src/mitm/targets/kiro.ts +64 -0
  2060. package/src/mitm/targets/openCode.ts +34 -0
  2061. package/src/mitm/targets/trae.ts +32 -0
  2062. package/src/mitm/targets/zed.ts +32 -0
  2063. package/src/mitm/tproxy/caTrust.ts +120 -0
  2064. package/src/mitm/tproxy/captureManager.ts +118 -0
  2065. package/src/mitm/tproxy/captureMode.ts +226 -0
  2066. package/src/mitm/tproxy/commands.ts +122 -0
  2067. package/src/mitm/tproxy/dynamicCert.ts +119 -0
  2068. package/src/mitm/tproxy/native/README.md +55 -0
  2069. package/src/mitm/tproxy/native/binding.gyp +9 -0
  2070. package/src/mitm/tproxy/native/transparent.c +147 -0
  2071. package/src/mitm/tproxy/setup.ts +68 -0
  2072. package/src/mitm/tproxy/tlsCapture.ts +368 -0
  2073. package/src/mitm/tproxy/transparentSocket.ts +131 -0
  2074. package/src/mitm/types.ts +75 -0
  2075. package/src/mitm/upstreamTrust.ts +40 -0
  2076. package/src/models/index.ts +29 -0
  2077. package/src/server/auth/loginGuard.ts +130 -0
  2078. package/src/server/authz/accessScopes.ts +62 -0
  2079. package/src/server/authz/accessTokenAuth.ts +67 -0
  2080. package/src/server/authz/assertAuth.ts +88 -0
  2081. package/src/server/authz/classify.ts +135 -0
  2082. package/src/server/authz/context.ts +44 -0
  2083. package/src/server/authz/csrf.ts +85 -0
  2084. package/src/server/authz/headers.ts +76 -0
  2085. package/src/server/authz/peerStamp.ts +99 -0
  2086. package/src/server/authz/pipeline.ts +370 -0
  2087. package/src/server/authz/policies/clientApi.ts +72 -0
  2088. package/src/server/authz/policies/management.ts +298 -0
  2089. package/src/server/authz/policies/public.ts +9 -0
  2090. package/src/server/authz/routeGuard.ts +233 -0
  2091. package/src/server/authz/types.ts +77 -0
  2092. package/src/server/cors/origins.ts +172 -0
  2093. package/src/server/origin/publicOrigin.ts +270 -0
  2094. package/src/server/ws/liveServer.ts +630 -0
  2095. package/src/server/ws/liveServerAllowList.ts +106 -0
  2096. package/src/server/ws/types.ts +57 -0
  2097. package/src/shared/components/Avatar.tsx +81 -0
  2098. package/src/shared/components/Badge.tsx +68 -0
  2099. package/src/shared/components/Breadcrumbs.tsx +103 -0
  2100. package/src/shared/components/Button.tsx +85 -0
  2101. package/src/shared/components/Card.tsx +141 -0
  2102. package/src/shared/components/Checkbox.tsx +38 -0
  2103. package/src/shared/components/CloudSyncStatus.tsx +104 -0
  2104. package/src/shared/components/Collapsible.tsx +93 -0
  2105. package/src/shared/components/CollapsibleSection.tsx +66 -0
  2106. package/src/shared/components/ColumnToggle.tsx +100 -0
  2107. package/src/shared/components/CommandPalette.tsx +375 -0
  2108. package/src/shared/components/ConsoleLogViewer.tsx +326 -0
  2109. package/src/shared/components/CursorAuthModal.tsx +198 -0
  2110. package/src/shared/components/DataTable.tsx +195 -0
  2111. package/src/shared/components/DegradationBadge.tsx +41 -0
  2112. package/src/shared/components/DistributeProxiesButton.tsx +74 -0
  2113. package/src/shared/components/EmptyState.tsx +121 -0
  2114. package/src/shared/components/ErrorPageScaffold.tsx +91 -0
  2115. package/src/shared/components/FilterBar.tsx +197 -0
  2116. package/src/shared/components/Footer.tsx +169 -0
  2117. package/src/shared/components/Header.tsx +284 -0
  2118. package/src/shared/components/InfoTooltip.tsx +34 -0
  2119. package/src/shared/components/Input.tsx +155 -0
  2120. package/src/shared/components/KiroAuthModal.tsx +425 -0
  2121. package/src/shared/components/KiroOAuthWrapper.tsx +116 -0
  2122. package/src/shared/components/KiroSocialOAuthModal.tsx +202 -0
  2123. package/src/shared/components/LanguageSelector.tsx +106 -0
  2124. package/src/shared/components/LinkifiedText.tsx +30 -0
  2125. package/src/shared/components/Loading.tsx +128 -0
  2126. package/src/shared/components/LocaleAutoDetect.tsx +43 -0
  2127. package/src/shared/components/MaintenanceBanner.tsx +87 -0
  2128. package/src/shared/components/ManualConfigModal.tsx +51 -0
  2129. package/src/shared/components/Modal.tsx +240 -0
  2130. package/src/shared/components/ModelRoutingSection.tsx +323 -0
  2131. package/src/shared/components/ModelSelectModal.tsx +618 -0
  2132. package/src/shared/components/MonacoEditor.tsx +23 -0
  2133. package/src/shared/components/NavigationProgress.tsx +105 -0
  2134. package/src/shared/components/NoAuthAccountCard.tsx +539 -0
  2135. package/src/shared/components/NoAuthProviderCard.tsx +40 -0
  2136. package/src/shared/components/NoAuthProviderToggle.tsx +36 -0
  2137. package/src/shared/components/NotificationToast.tsx +206 -0
  2138. package/src/shared/components/OAuthModal.tsx +992 -0
  2139. package/src/shared/components/OmniRouteLogo.tsx +86 -0
  2140. package/src/shared/components/PresetSlider.tsx +64 -0
  2141. package/src/shared/components/PricingModal.tsx +211 -0
  2142. package/src/shared/components/ProviderIcon.tsx +263 -0
  2143. package/src/shared/components/ProviderTestSlideOver.tsx +560 -0
  2144. package/src/shared/components/ProxyConfigModal.tsx +781 -0
  2145. package/src/shared/components/ProxyLogDetail.tsx +192 -0
  2146. package/src/shared/components/ProxyLogger.tsx +573 -0
  2147. package/src/shared/components/PwaRegister.tsx +22 -0
  2148. package/src/shared/components/RequestLoggerDetail.tsx +797 -0
  2149. package/src/shared/components/RequestLoggerV2.tsx +1628 -0
  2150. package/src/shared/components/RiskNoticeModal.tsx +81 -0
  2151. package/src/shared/components/SegmentedControl.tsx +69 -0
  2152. package/src/shared/components/Select.tsx +110 -0
  2153. package/src/shared/components/Sidebar.tsx +726 -0
  2154. package/src/shared/components/SkillsConceptCard.tsx +84 -0
  2155. package/src/shared/components/SystemMonitor.tsx +179 -0
  2156. package/src/shared/components/Textarea.tsx +29 -0
  2157. package/src/shared/components/ThemeProvider.tsx +14 -0
  2158. package/src/shared/components/ThemeToggle.tsx +52 -0
  2159. package/src/shared/components/Toggle.tsx +102 -0
  2160. package/src/shared/components/TokenHealthBadge.tsx +113 -0
  2161. package/src/shared/components/Tooltip.tsx +193 -0
  2162. package/src/shared/components/TraeAuthModal.tsx +334 -0
  2163. package/src/shared/components/UsageAnalytics.tsx +414 -0
  2164. package/src/shared/components/UsageStats.tsx +693 -0
  2165. package/src/shared/components/analytics/ApiKeyFilterDropdown.tsx +205 -0
  2166. package/src/shared/components/analytics/CustomRangePicker.tsx +195 -0
  2167. package/src/shared/components/analytics/TimeRangeSelector.tsx +49 -0
  2168. package/src/shared/components/analytics/chartColors.ts +12 -0
  2169. package/src/shared/components/analytics/charts.tsx +1023 -0
  2170. package/src/shared/components/analytics/index.tsx +28 -0
  2171. package/src/shared/components/analytics/rechartsCore.tsx +85 -0
  2172. package/src/shared/components/analytics/rechartsDonuts.tsx +195 -0
  2173. package/src/shared/components/analytics/rechartsUsageCharts.tsx +312 -0
  2174. package/src/shared/components/cli/CliComparisonCard.tsx +82 -0
  2175. package/src/shared/components/cli/CliConceptCard.tsx +58 -0
  2176. package/src/shared/components/cli/CliToolCard.tsx +152 -0
  2177. package/src/shared/components/cli/index.ts +8 -0
  2178. package/src/shared/components/compression/CompressionPipelineEditor.tsx +175 -0
  2179. package/src/shared/components/compression/EngineConfigForm.tsx +78 -0
  2180. package/src/shared/components/compression/EngineConfigPage.tsx +411 -0
  2181. package/src/shared/components/compression/compressionPipelineModel.ts +78 -0
  2182. package/src/shared/components/docs/APIReference.stories.tsx +35 -0
  2183. package/src/shared/components/docs/APIReference.tsx +83 -0
  2184. package/src/shared/components/docs/Callout.stories.tsx +40 -0
  2185. package/src/shared/components/docs/Callout.tsx +38 -0
  2186. package/src/shared/components/docs/CodeBlock.stories.tsx +25 -0
  2187. package/src/shared/components/docs/CodeBlock.tsx +52 -0
  2188. package/src/shared/components/docs/DocsBreadcrumbs.stories.tsx +20 -0
  2189. package/src/shared/components/docs/DocsBreadcrumbs.tsx +46 -0
  2190. package/src/shared/components/docs/DocsSidebar.stories.tsx +31 -0
  2191. package/src/shared/components/docs/DocsSidebar.tsx +77 -0
  2192. package/src/shared/components/docs/DocsThemeProvider.tsx +21 -0
  2193. package/src/shared/components/docs/Tabs.stories.tsx +26 -0
  2194. package/src/shared/components/docs/Tabs.tsx +50 -0
  2195. package/src/shared/components/docs/tokens.ts +38 -0
  2196. package/src/shared/components/flow/FlowCanvas.tsx +151 -0
  2197. package/src/shared/components/flow/StatusDot.tsx +36 -0
  2198. package/src/shared/components/flow/edgeStyles.ts +32 -0
  2199. package/src/shared/components/index.tsx +49 -0
  2200. package/src/shared/components/layouts/AuthLayout.tsx +28 -0
  2201. package/src/shared/components/layouts/DashboardLayout.tsx +131 -0
  2202. package/src/shared/components/layouts/index.tsx +3 -0
  2203. package/src/shared/components/lobeProviderIcons.ts +488 -0
  2204. package/src/shared/components/logTableStyles.ts +7 -0
  2205. package/src/shared/components/modelSelectModalHelpers.ts +75 -0
  2206. package/src/shared/components/oauthBlobSubmit.ts +42 -0
  2207. package/src/shared/components/proxyAssignment.ts +40 -0
  2208. package/src/shared/components/requestLoggerPreferences.ts +44 -0
  2209. package/src/shared/components/requestLoggerSignature.ts +56 -0
  2210. package/src/shared/constants/agentSkills.ts +457 -0
  2211. package/src/shared/constants/antigravityClientProfile.ts +29 -0
  2212. package/src/shared/constants/apiKeyPolicyScopes.ts +5 -0
  2213. package/src/shared/constants/appConfig.ts +12 -0
  2214. package/src/shared/constants/batch.ts +1 -0
  2215. package/src/shared/constants/batchEndpoints.ts +11 -0
  2216. package/src/shared/constants/bodySize.ts +39 -0
  2217. package/src/shared/constants/cliCompatProviders.ts +88 -0
  2218. package/src/shared/constants/cliTools.ts +862 -0
  2219. package/src/shared/constants/clientIdentityProfiles.ts +89 -0
  2220. package/src/shared/constants/colors.ts +147 -0
  2221. package/src/shared/constants/comboConfigMode.ts +9 -0
  2222. package/src/shared/constants/config.ts +44 -0
  2223. package/src/shared/constants/dashboardCsrf.ts +1 -0
  2224. package/src/shared/constants/endpointCategories.ts +133 -0
  2225. package/src/shared/constants/errorCodes.ts +245 -0
  2226. package/src/shared/constants/featureFlagDefinitions.ts +495 -0
  2227. package/src/shared/constants/headers.ts +16 -0
  2228. package/src/shared/constants/homeWidgets.ts +5 -0
  2229. package/src/shared/constants/index.ts +4 -0
  2230. package/src/shared/constants/managementScopes.ts +31 -0
  2231. package/src/shared/constants/mcpScopes.ts +78 -0
  2232. package/src/shared/constants/mitmToolHosts.ts +31 -0
  2233. package/src/shared/constants/modal.ts +5 -0
  2234. package/src/shared/constants/modelCompat.ts +9 -0
  2235. package/src/shared/constants/modelSpecs.ts +607 -0
  2236. package/src/shared/constants/models.ts +40 -0
  2237. package/src/shared/constants/openRouterPreset.ts +11 -0
  2238. package/src/shared/constants/pricing/default-pricing.ts +16 -0
  2239. package/src/shared/constants/pricing/frontier-labs.ts +386 -0
  2240. package/src/shared/constants/pricing/inference-hosts.ts +208 -0
  2241. package/src/shared/constants/pricing/oauth-subscriptions.ts +599 -0
  2242. package/src/shared/constants/pricing/regional.ts +168 -0
  2243. package/src/shared/constants/pricing/shared-tiers.ts +162 -0
  2244. package/src/shared/constants/pricing.ts +40 -0
  2245. package/src/shared/constants/providers/apikey/enterprise-cloud.ts +214 -0
  2246. package/src/shared/constants/providers/apikey/frontier-labs.ts +253 -0
  2247. package/src/shared/constants/providers/apikey/gateways.ts +662 -0
  2248. package/src/shared/constants/providers/apikey/index.ts +21 -0
  2249. package/src/shared/constants/providers/apikey/inference-hosts.ts +328 -0
  2250. package/src/shared/constants/providers/apikey/regional.ts +372 -0
  2251. package/src/shared/constants/providers/apikey/specialty-media.ts +266 -0
  2252. package/src/shared/constants/providers/audio.ts +71 -0
  2253. package/src/shared/constants/providers/cloud-agent.ts +36 -0
  2254. package/src/shared/constants/providers/local.ts +162 -0
  2255. package/src/shared/constants/providers/noauth.ts +123 -0
  2256. package/src/shared/constants/providers/oauth.ts +231 -0
  2257. package/src/shared/constants/providers/search.ts +125 -0
  2258. package/src/shared/constants/providers/system.ts +16 -0
  2259. package/src/shared/constants/providers/upstream-proxy.ts +36 -0
  2260. package/src/shared/constants/providers/web-cookie.ts +364 -0
  2261. package/src/shared/constants/providers.ts +449 -0
  2262. package/src/shared/constants/publicApiRoutes.ts +62 -0
  2263. package/src/shared/constants/responsesPreviousResponseId.ts +5 -0
  2264. package/src/shared/constants/routingStrategies.ts +216 -0
  2265. package/src/shared/constants/selfServiceScopes.ts +20 -0
  2266. package/src/shared/constants/serviceKinds.ts +34 -0
  2267. package/src/shared/constants/sidebarGroupVisibility.ts +31 -0
  2268. package/src/shared/constants/sidebarVisibility/sections.ts +769 -0
  2269. package/src/shared/constants/sidebarVisibility/types.ts +161 -0
  2270. package/src/shared/constants/sidebarVisibility.ts +291 -0
  2271. package/src/shared/constants/spawnCapablePrefixes.ts +35 -0
  2272. package/src/shared/constants/statusColors.ts +17 -0
  2273. package/src/shared/constants/upstreamHeaders.ts +42 -0
  2274. package/src/shared/constants/visionBridgeDefaults.ts +88 -0
  2275. package/src/shared/constants/visionModels.ts +68 -0
  2276. package/src/shared/contracts/quota.ts +138 -0
  2277. package/src/shared/hooks/cli/useToolBatchStatuses.ts +54 -0
  2278. package/src/shared/hooks/index.ts +3 -0
  2279. package/src/shared/hooks/useCopyToClipboard.ts +38 -0
  2280. package/src/shared/hooks/useDisplayBaseUrl.ts +49 -0
  2281. package/src/shared/hooks/useElectron.ts +197 -0
  2282. package/src/shared/hooks/useTheme.ts +59 -0
  2283. package/src/shared/http/apiErrorMessage.ts +19 -0
  2284. package/src/shared/lib/persistLocale.ts +19 -0
  2285. package/src/shared/middleware/bodySizeGuard.ts +97 -0
  2286. package/src/shared/middleware/chatBodyAdmission.ts +161 -0
  2287. package/src/shared/middleware/correlationId.ts +43 -0
  2288. package/src/shared/network/outboundUrlGuard.ts +276 -0
  2289. package/src/shared/network/remoteImageFetch.ts +182 -0
  2290. package/src/shared/network/safeOutboundFetch.ts +372 -0
  2291. package/src/shared/providers/webSessionCredentials.ts +308 -0
  2292. package/src/shared/reasoning/effortStandardization.ts +124 -0
  2293. package/src/shared/schemas/agentBridge.ts +37 -0
  2294. package/src/shared/schemas/cliCatalog.ts +66 -0
  2295. package/src/shared/schemas/inspector.ts +47 -0
  2296. package/src/shared/schemas/memory.ts +142 -0
  2297. package/src/shared/schemas/playground.ts +67 -0
  2298. package/src/shared/schemas/qdrant.ts +39 -0
  2299. package/src/shared/schemas/quota.ts +61 -0
  2300. package/src/shared/schemas/searchTools.ts +39 -0
  2301. package/src/shared/schemas/validation.ts +154 -0
  2302. package/src/shared/services/apiKeyResolver.ts +44 -0
  2303. package/src/shared/services/backupService.ts +233 -0
  2304. package/src/shared/services/claudeCliConfig.ts +17 -0
  2305. package/src/shared/services/cliRuntime.ts +1099 -0
  2306. package/src/shared/services/cloudSyncScheduler.ts +135 -0
  2307. package/src/shared/services/droidCustomModels.ts +118 -0
  2308. package/src/shared/services/initializeCloudSync.ts +31 -0
  2309. package/src/shared/services/loginShellPath.ts +87 -0
  2310. package/src/shared/services/modelSyncScheduler.ts +225 -0
  2311. package/src/shared/services/opencodeConfig.ts +140 -0
  2312. package/src/shared/services/providerLimitsSyncScheduler.ts +72 -0
  2313. package/src/shared/types/cliBatchStatus.ts +18 -0
  2314. package/src/shared/types/index.ts +3 -0
  2315. package/src/shared/types/pagination.ts +61 -0
  2316. package/src/shared/types/utilization.ts +405 -0
  2317. package/src/shared/utils/a11yAudit.ts +140 -0
  2318. package/src/shared/utils/api.ts +112 -0
  2319. package/src/shared/utils/apiAuth.ts +354 -0
  2320. package/src/shared/utils/apiKey.ts +92 -0
  2321. package/src/shared/utils/apiKeyPolicy.ts +680 -0
  2322. package/src/shared/utils/apiResponse.ts +79 -0
  2323. package/src/shared/utils/bulkApiKeyParser.ts +113 -0
  2324. package/src/shared/utils/circuitBreaker.ts +611 -0
  2325. package/src/shared/utils/classify429.ts +253 -0
  2326. package/src/shared/utils/cliCompat.ts +5 -0
  2327. package/src/shared/utils/clineAuth.ts +62 -0
  2328. package/src/shared/utils/clipboard.ts +49 -0
  2329. package/src/shared/utils/cloud.ts +41 -0
  2330. package/src/shared/utils/cn.ts +12 -0
  2331. package/src/shared/utils/codexBaseUrl.ts +32 -0
  2332. package/src/shared/utils/codexConfig.ts +30 -0
  2333. package/src/shared/utils/compressionHeaderEcho.ts +59 -0
  2334. package/src/shared/utils/cors.ts +24 -0
  2335. package/src/shared/utils/costEstimator.ts +126 -0
  2336. package/src/shared/utils/dashboardCsrf.ts +196 -0
  2337. package/src/shared/utils/featureFlags.ts +113 -0
  2338. package/src/shared/utils/fetchError.ts +26 -0
  2339. package/src/shared/utils/fetchTimeout.ts +78 -0
  2340. package/src/shared/utils/formatting.ts +183 -0
  2341. package/src/shared/utils/freeModels.ts +112 -0
  2342. package/src/shared/utils/index.ts +39 -0
  2343. package/src/shared/utils/inputSanitizer.ts +339 -0
  2344. package/src/shared/utils/linkify.ts +54 -0
  2345. package/src/shared/utils/logRedaction.ts +108 -0
  2346. package/src/shared/utils/logger.ts +173 -0
  2347. package/src/shared/utils/machine.ts +5 -0
  2348. package/src/shared/utils/machineId.ts +167 -0
  2349. package/src/shared/utils/maskEmail.ts +77 -0
  2350. package/src/shared/utils/modelCatalogSearch.ts +84 -0
  2351. package/src/shared/utils/noAuthProviders.ts +79 -0
  2352. package/src/shared/utils/nodeRuntimeSupport.ts +112 -0
  2353. package/src/shared/utils/parseApiKeys.ts +41 -0
  2354. package/src/shared/utils/providerDisplayLabel.ts +41 -0
  2355. package/src/shared/utils/providerHints.ts +73 -0
  2356. package/src/shared/utils/providerModelAliases.ts +68 -0
  2357. package/src/shared/utils/rateLimiter.ts +243 -0
  2358. package/src/shared/utils/releaseNotes.ts +53 -0
  2359. package/src/shared/utils/requestId.ts +102 -0
  2360. package/src/shared/utils/requestTelemetry.ts +189 -0
  2361. package/src/shared/utils/requestTimeout.ts +64 -0
  2362. package/src/shared/utils/resolveOmniRouteBaseUrl.ts +24 -0
  2363. package/src/shared/utils/runtimeTimeouts.ts +257 -0
  2364. package/src/shared/utils/secretsValidator.ts +135 -0
  2365. package/src/shared/utils/secureRandom.ts +66 -0
  2366. package/src/shared/utils/serviceTierLabels.ts +42 -0
  2367. package/src/shared/utils/shuffleDeck.ts +147 -0
  2368. package/src/shared/utils/sidebarRouteMatch.ts +43 -0
  2369. package/src/shared/utils/streamTracker.ts +187 -0
  2370. package/src/shared/utils/structuredLogger.ts +219 -0
  2371. package/src/shared/utils/tiktokenCounter.ts +21 -0
  2372. package/src/shared/utils/turkishText.ts +66 -0
  2373. package/src/shared/utils/upstreamError.ts +112 -0
  2374. package/src/shared/validation/compressionConfigSchemas.ts +234 -0
  2375. package/src/shared/validation/freeProxySchemas.ts +111 -0
  2376. package/src/shared/validation/helpers.ts +117 -0
  2377. package/src/shared/validation/oneproxySchemas.ts +14 -0
  2378. package/src/shared/validation/providerSchema.ts +51 -0
  2379. package/src/shared/validation/providerSpecificData.ts +354 -0
  2380. package/src/shared/validation/schemas/apiV1.ts +297 -0
  2381. package/src/shared/validation/schemas/auth.ts +200 -0
  2382. package/src/shared/validation/schemas/cli.ts +86 -0
  2383. package/src/shared/validation/schemas/cloud.ts +53 -0
  2384. package/src/shared/validation/schemas/combo.ts +373 -0
  2385. package/src/shared/validation/schemas/evals.ts +86 -0
  2386. package/src/shared/validation/schemas/gemini.ts +59 -0
  2387. package/src/shared/validation/schemas/keys.ts +144 -0
  2388. package/src/shared/validation/schemas/misc.ts +203 -0
  2389. package/src/shared/validation/schemas/payloadRules.ts +64 -0
  2390. package/src/shared/validation/schemas/pricing.ts +40 -0
  2391. package/src/shared/validation/schemas/provider.ts +430 -0
  2392. package/src/shared/validation/schemas/proxy.ts +243 -0
  2393. package/src/shared/validation/schemas/routing.ts +88 -0
  2394. package/src/shared/validation/schemas/settings.ts +267 -0
  2395. package/src/shared/validation/schemas/translator.ts +44 -0
  2396. package/src/shared/validation/schemas.ts +18 -0
  2397. package/src/shared/validation/settingsSchemas.ts +396 -0
  2398. package/src/sse/handlers/chat.ts +1750 -0
  2399. package/src/sse/handlers/chatHelpers.ts +859 -0
  2400. package/src/sse/handlers/requestBody.ts +24 -0
  2401. package/src/sse/handlers/resolveRoutingModel.ts +17 -0
  2402. package/src/sse/services/auth.ts +2446 -0
  2403. package/src/sse/services/cooldownAwareRetry.ts +162 -0
  2404. package/src/sse/services/model.ts +254 -0
  2405. package/src/sse/services/noAuthProviderSettings.ts +18 -0
  2406. package/src/sse/services/noAuthProxyResolution.ts +111 -0
  2407. package/src/sse/services/sessionAffinityPin.ts +247 -0
  2408. package/src/sse/services/streamState.ts +218 -0
  2409. package/src/sse/services/tokenRefresh.ts +275 -0
  2410. package/src/sse/utils/logger.ts +37 -0
  2411. package/src/types/databaseSettings.ts +124 -0
  2412. package/src/types/global.d.ts +122 -0
  2413. package/src/types/index.ts +10 -0
  2414. package/src/types/provider.ts +9 -0
  2415. package/src/types/sqljs.d.ts +32 -0
@@ -0,0 +1,4452 @@
1
+ import { injectMemoryAndSkills } from "./chatCore/memorySkillsInjection.ts";
2
+ import { resolveChatCoreRequestSetup } from "./chatCore/requestSetup.ts";
3
+ import { buildFailureUsageRecord } from "./chatCore/failureUsage.ts";
4
+ import { extractSystemRoleMessages } from "./chatCore/claudeSystemRole.ts";
5
+ export { extractSystemRoleMessages } from "./chatCore/claudeSystemRole.ts";
6
+ import { checkIdempotencyCache } from "./chatCore/idempotency.ts";
7
+ import { checkSemanticCache } from "./chatCore/semanticCache.ts";
8
+ import {
9
+ shouldDefaultAllowClassifier,
10
+ buildDefaultAllowClaudeMessage,
11
+ } from "./chatCore/claudeClassifierCompat.ts";
12
+ import { applyClientUsageBuffer } from "./chatCore/clientUsageBuffer.ts";
13
+ import { buildPostCallGuardrailContext } from "./chatCore/postCallGuardrailContext.ts";
14
+ import { storeSemanticCacheResponse } from "./chatCore/semanticCacheStore.ts";
15
+ import { buildNonStreamingResponseHeaders } from "./chatCore/nonStreamingResponseHeaders.ts";
16
+ import { buildNonStreamingJsonResponse } from "./chatCore/nonStreamingJsonResponse.ts";
17
+ import { maybeConvertJsonBodyToSse } from "./chatCore/jsonBodyToSse.ts";
18
+ import { assembleStreamingResponseHeaders } from "./chatCore/streamingResponseHeaders.ts";
19
+ import { storeStreamingSemanticCacheResponse } from "./chatCore/streamingSemanticCacheStore.ts";
20
+ import { assembleStreamingPipeline } from "./chatCore/streamingPipeline.ts";
21
+ import { sanitizeChatRequestBody } from "./chatCore/sanitization.ts";
22
+ import {
23
+ getHeaderValueCaseInsensitive,
24
+ isNoMemoryRequested,
25
+ resolveCompressionHeader,
26
+ isStripReasoningRequested,
27
+ } from "./chatCore/headers.ts";
28
+ import { markCodexScopeRateLimited } from "./chatCore/codexFailover.ts";
29
+ import { trackDevice, extractIpFromHeaders } from "../services/deviceTracker.ts";
30
+ import { getCombosCached } from "./chatCore/comboContextCache.ts";
31
+ export { clearCombosCache, clearUpstreamProxyConfigCache } from "./chatCore/comboContextCache.ts";
32
+ import {
33
+ resolveAccountSemaphoreKey,
34
+ resolveAccountSemaphoreMaxConcurrency,
35
+ buildClaudePromptCacheLogMeta,
36
+ } from "./chatCore/executorHelpers.ts";
37
+ import {
38
+ shouldUseNativeCodexPassthrough,
39
+ redactPassthroughThinkingSignatures,
40
+ isClaudeCodeSemanticPassthroughRequest,
41
+ } from "./chatCore/passthroughHelpers.ts";
42
+ import {
43
+ buildStreamingResponseHeaders,
44
+ materializeDeduplicatedExecutionResult,
45
+ stripNextMiddlewareControlHeaders,
46
+ stripStaleForwardingHeaders,
47
+ } from "./chatCore/responseHeaders.ts";
48
+ import {
49
+ forwardDashboardEventToLiveWs,
50
+ maybeSyncClaudeExtraUsageState,
51
+ } from "./chatCore/telemetryHelpers.ts";
52
+ // Re-export the previously inline-defined helpers so existing importers of these
53
+ // symbols from chatCore.ts (tests, sibling modules) keep resolving after the split.
54
+ export {
55
+ shouldUseNativeCodexPassthrough,
56
+ redactPassthroughThinkingSignatures,
57
+ isClaudeCodeSemanticPassthroughRequest,
58
+ buildStreamingResponseHeaders,
59
+ stripStaleForwardingHeaders,
60
+ };
61
+ import {
62
+ extractMemoryTextFromResponse,
63
+ extractMemoryTextFromRequestBody,
64
+ resolveMemoryOwnerId,
65
+ } from "./chatCore/memoryExtraction.ts";
66
+ import { CORS_HEADERS } from "../utils/cors.ts";
67
+ import { checkHeapPressureGuard } from "../utils/heapPressure.ts";
68
+ import { normalizeHeaders } from "../utils/headers.ts";
69
+ import { resolveChatCoreRequestFormat } from "./chatCore/requestFormat.ts";
70
+ import { resolveChatCoreTargetFormat } from "./chatCore/targetFormat.ts";
71
+ import { defaultClaudeToolType } from "./chatCore/claudeToolDefaults.ts";
72
+ import { injectSystemPrompt, injectCustomSystemPrompt } from "../services/systemPrompt.ts";
73
+ import { translateRequest, needsTranslation } from "../translator/index.ts";
74
+ import { FORMATS } from "../translator/formats.ts";
75
+ import { sanitizeKiroTools } from "../utils/kiroSanitizer.ts";
76
+ import { splitMisplacedToolResults } from "../translator/helpers/claudeHelper.ts";
77
+ import {
78
+ createSSETransformStreamWithLogger,
79
+ createPassthroughStreamWithLogger,
80
+ COLORS,
81
+ withBodyTimeout,
82
+ } from "../utils/stream.ts";
83
+ import { ensureStreamReadiness } from "../utils/streamReadiness.ts";
84
+ import { resolveSuppressThinkClose, THINKING_MARKER_HEADER } from "../utils/thinkCloseMarker.ts";
85
+ import { resolveStreamReadinessTimeout } from "../utils/streamReadinessPolicy.ts";
86
+ import { resolveAgentGoalPolicy } from "../utils/agentGoalPolicy.ts";
87
+ import { createStreamController } from "../utils/streamHandler.ts";
88
+ import * as streamFailure from "../utils/streamFailureFinalization.ts";
89
+ import { createSseHeartbeatTransform, shapeForClientFormat } from "../utils/sseHeartbeat.ts";
90
+ import { addBufferToUsage, filterUsageForFormat, estimateUsage } from "../utils/usageTracking.ts";
91
+ import {
92
+ refreshWithRetry,
93
+ isUnrecoverableRefreshError,
94
+ runWithOnPersist,
95
+ runWithCasGuard,
96
+ } from "../services/tokenRefresh.ts";
97
+ import { createRequestLogger } from "../utils/requestLogger.ts";
98
+ import { createPreparedRequestLogger, runWithCapture } from "../utils/providerRequestLogging.ts";
99
+ import { summarizeToolSources } from "../utils/toolSources.ts";
100
+ import { applyResponsesPreviousResponseIdPolicy } from "../utils/responsesStatePolicy.ts";
101
+ import { applyClaudeEffortVariant } from "./chatCore/claudeEffortVariant.ts";
102
+ import { DEFAULT_THINKING_CLAUDE_SIGNATURE } from "../config/defaultThinkingSignature.ts";
103
+ import {
104
+ getStripTypesForProviderModel,
105
+ stripIncompatibleMessageContent,
106
+ } from "../services/modelStrip.ts";
107
+ import { resolveModelAlias } from "../services/modelDeprecation.ts";
108
+ import { normalizeMimoThinking } from "../services/mimoThinking.ts";
109
+ import { normalizeClaudeAdaptiveThinking } from "../services/claudeAdaptiveThinking.ts";
110
+ import { normalizeClaudeHaikuConstraints } from "../services/claudeHaikuConstraints.ts";
111
+ import { echoModelInObject } from "../services/responseModelEcho.ts";
112
+ import { stripGpt5SamplingWhenReasoning } from "../services/gpt5SamplingGuard.ts";
113
+ import { getUnsupportedParams, REGISTRY } from "../config/providerRegistry.ts";
114
+ import { supportsMaxTokens } from "@/lib/modelCapabilities.ts";
115
+ import { normalizeThinkingForModel } from "@/shared/constants/modelSpecs.ts";
116
+ import {
117
+ buildErrorBody,
118
+ createErrorResult,
119
+ parseUpstreamError,
120
+ formatProviderError,
121
+ sanitizeErrorMessage,
122
+ } from "../utils/error.ts";
123
+ import { reportMalformed200, detectMalformedNonStream } from "../utils/diagnostics.ts";
124
+ import {
125
+ checkTokenLimits,
126
+ recordTokenUsage,
127
+ } from "@omniroute/open-sse/services/tokenLimitCounter.ts";
128
+ import {
129
+ COOLDOWN_MS,
130
+ HTTP_STATUS,
131
+ FETCH_BODY_TIMEOUT_MS,
132
+ PROVIDER_MAX_TOKENS,
133
+ STREAM_IDLE_TIMEOUT_MS,
134
+ STREAM_READINESS_MAX_TIMEOUT_MS,
135
+ STREAM_READINESS_TIMEOUT_MS,
136
+ ANTIGRAVITY_PRE_RESPONSE_TIMEOUT_CODE,
137
+ STREAM_RECOVERY,
138
+ } from "../config/constants.ts";
139
+ import { createRecoverableStream, makeContinuationBody } from "../services/streamRecovery.ts";
140
+ import {
141
+ resolveResilienceSettings,
142
+ isStreamRecoveryExplicitlyConfigured,
143
+ } from "@/lib/resilience/settings";
144
+ import {
145
+ classifyProviderError,
146
+ PROVIDER_ERROR_TYPES,
147
+ isEmptyContentResponse,
148
+ } from "../services/errorClassifier.ts";
149
+ import { updateProviderConnection, getProviderConnectionById } from "@/lib/db/providers";
150
+ import { wasRefreshTokenRotated } from "@omniroute/open-sse/services/refreshSerializer.ts";
151
+ import { connectionHasExtraKeys } from "../services/apiKeyRotator.ts";
152
+ import { recordKeyHealthStatus as recordKeyHealthStatusFor } from "./chatCore/keyHealth.ts";
153
+ import { getSkillsModelIdForFormat } from "./chatCore/skillsFormat.ts";
154
+ import { readNonStreamingResponseBody } from "./chatCore/nonStreamingResponseBody.ts";
155
+ import {
156
+ isSemaphoreCapacityError,
157
+ createStreamingErrorResult,
158
+ getUpstreamErrorIdentifier,
159
+ } from "./chatCore/streamErrorResult.ts";
160
+ import { wrapReadableStreamWithFinalize } from "./chatCore/streamFinalize.ts";
161
+ import { buildCacheUsageLogMeta } from "./chatCore/cacheUsageMeta.ts";
162
+ import { buildExecutorClientHeaders } from "./chatCore/executorClientHeaders.ts";
163
+ import { resolveExecutionCredentials as resolveExecutionCredentialsFor } from "./chatCore/executionCredentials.ts";
164
+ import { resolveExecutorWithProxy as resolveExecutorWithProxyFor } from "./chatCore/executorProxy.ts";
165
+ import type { ClaudeMessage } from "./chatCore/claudeMessageTypes.ts";
166
+ import { normalizeClaudeUpstreamMessages as normalizeClaudeUpstreamMessagesFor } from "./chatCore/claudeUpstreamMessages.ts";
167
+ import {
168
+ persistAttemptLogs as persistAttemptLogsFor,
169
+ type PersistAttemptLogsArgs,
170
+ } from "./chatCore/attemptLogging.ts";
171
+ import { stageTrace } from "./chatCore/stageTrace.ts";
172
+ import { attachCompressionUsageReceiptAfterAnalytics as attachCompressionUsageReceiptAfterAnalyticsFor } from "./chatCore/compressionUsageReceipt.ts";
173
+ import { prepareUpstreamBody } from "./chatCore/upstreamBody.ts";
174
+ import { getQuotaScopeLabelForProvider } from "../services/antigravityQuotaFamily.ts";
175
+
176
+ import {
177
+ getCallLogPipelineCaptureStreamChunks,
178
+ getCallLogPipelineMaxSizeBytes,
179
+ } from "@/lib/logEnv";
180
+ import { logAuditEvent } from "@/lib/compliance";
181
+ import { emit } from "@/lib/events/eventBus";
182
+ import { adaptBodyForCompression } from "../services/compression/bodyAdapter.ts";
183
+ import { ensureEngineBreakdown } from "../services/compression/engineBreakdown.ts";
184
+ import { handleBypassRequest } from "../utils/bypassHandler.ts";
185
+ import { saveRequestUsage, trackPendingRequest, appendRequestLog } from "@/lib/usageDb";
186
+ import { finalizePendingScope, updatePendingScope } from "@/lib/usage/pendingRequestScope";
187
+ import { recordCost } from "@/domain/costRules";
188
+ import { calculateCost } from "@/lib/usage/costCalculator";
189
+ import {
190
+ buildClaudePassthroughToolNameMap,
191
+ restoreClaudePassthroughToolNames,
192
+ mergeResponseToolNameMap,
193
+ } from "./chatCore/passthroughToolNames.ts";
194
+ import { resolveCompressionSettings } from "./chatCore/compressionSettings.ts";
195
+ import {
196
+ isBuiltinStackedPipeline,
197
+ isStackedCompressionCombo,
198
+ type RuntimeCompressionCombo,
199
+ } from "./chatCore/compressionComboPredicates.ts";
200
+ import { emitOutputStyleTelemetry } from "./chatCore/outputStyleTelemetry.ts";
201
+ import {
202
+ writeCompressionAnalytics,
203
+ writeCompressionSkip,
204
+ } from "./chatCore/compressionAnalyticsWrite.ts";
205
+ import { runPluginOnRequestHook } from "./chatCore/pluginOnRequest.ts";
206
+ import { recordContextEditingTelemetryHook } from "./chatCore/contextEditingTelemetry.ts";
207
+ import { recordCompressionCacheStats } from "./chatCore/compressionCacheStats.ts";
208
+ import { writeCavemanOutputAnalytics } from "./chatCore/cavemanOutputAnalytics.ts";
209
+ import { scheduleQuotaShareConsumption } from "./chatCore/quotaShareConsumption.ts";
210
+ import { emitRequestGamificationEvent } from "./chatCore/gamificationEvent.ts";
211
+ import { runPluginOnResponseHook } from "./chatCore/pluginOnResponse.ts";
212
+ import { scheduleStreamingQuotaShareConsumption } from "./chatCore/streamingQuotaShare.ts";
213
+ import { recordStreamingUsageStats } from "./chatCore/streamingUsageStats.ts";
214
+ import { recordStreamingCost } from "./chatCore/streamingCost.ts";
215
+ import {
216
+ appendNonStreamingSseTerminalSignal,
217
+ type NonStreamingSseTerminalState,
218
+ } from "./chatCore/nonStreamingSse.ts";
219
+ import { parseNonStreamingResponseBody } from "./chatCore/nonStreamingResponseParse.ts";
220
+ import { unwrapClinepassEnvelope } from "../utils/clinepassEnvelope.ts";
221
+ import { recordNonStreamingUsageStats } from "./chatCore/nonStreamingUsageStats.ts";
222
+ import {
223
+ createBodyTimeoutError,
224
+ readStreamChunkWithTimeout,
225
+ computeBillableTokens,
226
+ normalizeExecutorResult,
227
+ executeWithUpstreamStartTimeout,
228
+ } from "./chatCore/upstreamTimeouts.ts";
229
+ import { getModelNormalizeToolCallId, getModelPreserveOpenAIDeveloperRole } from "@/lib/localDb";
230
+ import { getProviderCredentials, extractSessionAffinityKey } from "@/sse/services/auth";
231
+ import { deleteSessionAccountAffinity } from "@/lib/db/sessionAccountAffinity";
232
+ import { getCacheControlSettings } from "@/lib/cacheControlSettings";
233
+ import { guardrailRegistry } from "@/lib/guardrails";
234
+ import { shouldPreserveCacheControl } from "../utils/cacheControlPolicy.ts";
235
+ import { getCachedSettings } from "@/lib/db/readCache";
236
+ import { applyCodexGlobalFastServiceTier } from "@/lib/providers/codexFastTier";
237
+ import { buildUpstreamHeadersForExecute as buildUpstreamHeadersForExecuteFor } from "./chatCore/upstreamExecuteHeaders.ts";
238
+ import {
239
+ resolveEffectiveServiceTier as resolveEffectiveServiceTierFor,
240
+ resolveReportedServiceTier as resolveReportedServiceTierFor,
241
+ type EffectiveServiceTier,
242
+ } from "./chatCore/serviceTier.ts";
243
+ import { cacheReasoningFromAssistantMessage } from "../services/reasoningCache.ts";
244
+ import { sanitizeOpenAITool } from "../services/toolSchemaSanitizer.ts";
245
+ import {
246
+ setDetectedToolLimit,
247
+ parseToolLimitFromError,
248
+ shouldDetectLimit,
249
+ } from "../services/toolLimitDetector.ts";
250
+
251
+ import { isCompactResponsesEndpoint } from "../executors/codex.ts";
252
+ import { buildCodexQuotaPersistence } from "./chatCore/codexQuota.ts";
253
+ import { invalidateCodexQuotaCache } from "../services/codexQuotaFetcher.ts";
254
+ import { translateNonStreamingResponse } from "./responseTranslator.ts";
255
+ import { unwrapClineNonStreamingEnvelope } from "./chatCore/clineResponseEnvelope.ts";
256
+ import { extractUsageFromResponse } from "./usageExtractor.ts";
257
+ import {
258
+ sanitizeOpenAIResponse,
259
+ sanitizeResponsesApiResponse,
260
+ shouldParseTextualReasoningTags,
261
+ } from "./responseSanitizer.ts";
262
+ import {
263
+ withRateLimit,
264
+ updateFromHeaders,
265
+ updateFromResponseBody,
266
+ initializeRateLimits,
267
+ } from "../services/rateLimitManager.ts";
268
+ import {
269
+ acquire as acquireAccountSemaphore,
270
+ markBlocked as markAccountSemaphoreBlocked,
271
+ } from "../services/accountSemaphore.ts";
272
+ import { lockModel, lockModelIfPerModelQuota } from "../services/accountFallback.ts";
273
+ import {
274
+ generateSignature,
275
+ getCachedResponse,
276
+ setCachedResponse,
277
+ isCacheableForRead,
278
+ isCacheableForWrite,
279
+ } from "@/lib/semanticCache";
280
+ import { saveIdempotency } from "@/lib/idempotencyLayer";
281
+ import {
282
+ isModelUnavailableError,
283
+ getNextFamilyFallback,
284
+ isContextOverflowError,
285
+ findLargerContextModel,
286
+ getModelFamily,
287
+ } from "../services/modelFamilyFallback.ts";
288
+ import { computeRequestHash, deduplicate, shouldDeduplicate } from "../services/requestDedup.ts";
289
+ import {
290
+ compressContext,
291
+ estimateTokens,
292
+ getTokenLimit,
293
+ resolveComboContextLimit,
294
+ } from "../services/contextManager.ts";
295
+ import { resolveBackgroundTaskRedirect } from "./chatCore/backgroundRedirect.ts";
296
+ import type { CompressionConfig, CompressionPipelineStep } from "../services/compression/types.ts";
297
+ import { prepareWebSearchFallbackBody } from "../services/webSearchFallback.ts";
298
+ import {
299
+ resolveExplicitStreamAlias,
300
+ resolveStreamFlag,
301
+ stripMarkdownCodeFence,
302
+ } from "../utils/aiSdkCompat.ts";
303
+ import { generateRequestId } from "@/shared/utils/requestId";
304
+ import { extractFacts } from "@/lib/memory/extraction";
305
+ import { handleToolCallExecution } from "@/lib/skills/interception";
306
+ import { OMNIROUTE_RESPONSE_HEADERS } from "@/shared/constants/headers";
307
+ import { getClaudeCodeCompatibleRequestDefaults } from "@/lib/providers/requestDefaults";
308
+ import {
309
+ buildClaudeCodeCompatibleRequest,
310
+ isClaudeCodeCompatibleProvider,
311
+ resolveClaudeCodeCompatibleSessionId,
312
+ } from "../services/claudeCodeCompatible.ts";
313
+ import { setGeminiThoughtSignatureMode } from "../services/geminiThoughtSignatureStore.ts";
314
+ import { fetchLiveProviderLimits } from "@/lib/usage/providerLimits";
315
+ import { isClaudeExtraUsageBlockEnabled } from "@/lib/providers/claudeExtraUsage";
316
+ import {
317
+ classifyModelScope429,
318
+ getModelScopeRetryDelayMs,
319
+ isModelScopeProvider,
320
+ } from "../services/modelscopePolicy.ts";
321
+ import { incrementRequestCount } from "../services/geminiRateLimitTracker.ts";
322
+
323
+ // ── Global memory pressure guard ────────────────────────────────────────
324
+ // Prevents OOM by rejecting new requests when V8 heap exceeds threshold.
325
+ // Self-healing: no counters to leak, no cleanup needed. The threshold
326
+ // auto-calibrates to 85% of the actual V8 heap ceiling (see heapPressure.ts) so
327
+ // it tracks --max-old-space-size across 1GB/2GB/large VPS instead of a fixed
328
+ // 200MB that sat below the app's own ~260MB baseline and rejected every request.
329
+
330
+ import { isSmallEnoughForSemanticCache } from "../utils/estimateSize.ts";
331
+
332
+ /**
333
+ * Core chat handler - shared between SSE and Worker
334
+ * Returns { success, response, status, error } for caller to handle fallback
335
+ * @param {object} options
336
+ * @param {object} options.body - Request body
337
+ * @param {object} options.modelInfo - { provider, model }
338
+ * @param {object} options.credentials - Provider credentials
339
+ * @param {object} options.log - Logger instance (optional)
340
+ * @param {function} options.onCredentialsRefreshed - Callback when credentials are refreshed
341
+ * @param {function} options.onRequestSuccess - Callback when request succeeds (to clear error status)
342
+ * @param {function} options.onDisconnect - Callback when client disconnects
343
+ * @param {string} options.connectionId - Connection ID for usage tracking
344
+ * @param {object} options.apiKeyInfo - API key metadata for usage attribution
345
+ * @param {string} options.userAgent - Client user agent for caching decisions
346
+ * @param {string} options.comboName - Combo name if this is a combo request
347
+ * @param {string} options.comboStrategy - Combo routing strategy (e.g., 'priority', 'cost-optimized')
348
+ * @param {boolean} options.isCombo - Whether this request is from a combo
349
+ * @param {string} options.connectionId - Connection ID for settings lookup
350
+ */
351
+
352
+ // extractSystemRoleMessages extracted to chatCore/claudeSystemRole.ts (#3501); re-exported above so
353
+ // existing importers (e.g. tests/unit/system-role-extraction.test.ts) keep resolving it from here.
354
+
355
+ export async function handleChatCore({
356
+ body,
357
+ modelInfo,
358
+ credentials,
359
+ log,
360
+ onCredentialsRefreshed,
361
+ onRequestSuccess,
362
+ onStreamFailure,
363
+ onDisconnect,
364
+ clientRawRequest,
365
+ connectionId,
366
+ apiKeyInfo = null,
367
+ userAgent,
368
+ comboName,
369
+ comboStrategy = null,
370
+ isCombo = false,
371
+ comboStepId = null,
372
+ comboExecutionKey = null,
373
+ cachedSettings = null,
374
+ skipUpstreamRetry = false,
375
+ createPiiTransform = null,
376
+ correlationId = null,
377
+ modelPinned = false,
378
+ }) {
379
+ let { provider, model, extendedContext } = modelInfo;
380
+ // ── Memory pressure guard ────────────────────────────────────────────
381
+ // Reject early if V8 heap is already near the 256MB limit. Prevents
382
+ // cascading OOM when many large-context requests arrive concurrently.
383
+ try {
384
+ const heapUsedMB = process.memoryUsage().heapUsed / (1024 * 1024);
385
+ const heapGuard = checkHeapPressureGuard(heapUsedMB);
386
+ if (heapGuard) return heapGuard;
387
+ } catch {
388
+ /* memoryUsage() never throws */
389
+ }
390
+
391
+ // Per-request model-routing metadata (first extracted slice of the request-setup phase).
392
+ const { apiFormat, customModelTargetFormat, requestedModel } = resolveChatCoreRequestSetup(
393
+ modelInfo,
394
+ body,
395
+ model
396
+ );
397
+ const isModelScope = () => isModelScopeProvider(provider, credentials?.providerSpecificData);
398
+ const startTime = Date.now();
399
+ // Per-request trace id + checkpoint helper. Lets us see exactly which await
400
+ // a hung request was sitting on in `[STAGE_TRACE]` log lines. Uses crypto RNG
401
+ // (not Math.random) purely to satisfy CodeQL js/insecure-randomness — this id
402
+ // is a log-correlation token, not a security secret.
403
+ const traceId = globalThis.crypto.randomUUID().slice(0, 6);
404
+
405
+ // Emit request.started event for real-time dashboard
406
+ setImmediate(() => {
407
+ emit("request.started", {
408
+ id: traceId,
409
+ model: model || "unknown",
410
+ provider: provider || "unknown",
411
+ timestamp: startTime,
412
+ comboName: comboName || undefined,
413
+ });
414
+ });
415
+ const traceEnabled = process.env.OMNIROUTE_TRACE === "true" || process.env.DEBUG === "true";
416
+ // Stage trace extracted to chatCore/stageTrace.ts (#3501); bind the per-request inputs once so the
417
+ // call sites stay byte-identical.
418
+ const trace = (label: string, extra?: Record<string, unknown>) =>
419
+ stageTrace(label, extra, { traceEnabled, startTime, traceId, log });
420
+ const getCurrentConnectionId = () => {
421
+ const credentialConnectionId =
422
+ typeof credentials?.connectionId === "string" && credentials.connectionId.trim().length > 0
423
+ ? credentials.connectionId.trim()
424
+ : null;
425
+ return credentialConnectionId || connectionId || null;
426
+ };
427
+ let tokensCompressed: number | null = null;
428
+ body = injectSystemPrompt(body);
429
+ // ── Per-endpoint custom system prompt (port of upstream #2063) ──
430
+ // Reads from cachedSettings if available (passed in from combo/chat layer)
431
+ // to avoid an extra DB read on the hot path. Falls through to getCachedSettings()
432
+ // only when this function is called outside the normal chat dispatch.
433
+ {
434
+ const _s = cachedSettings ?? (await getCachedSettings());
435
+ if (
436
+ _s.customSystemPromptEnabled === true &&
437
+ typeof _s.customSystemPrompt === "string" &&
438
+ _s.customSystemPrompt
439
+ ) {
440
+ body = injectCustomSystemPrompt(body as Record<string, unknown>, _s.customSystemPrompt);
441
+ log?.debug?.("CUSTOMSP", "custom system prompt injected");
442
+ }
443
+ }
444
+ // ── Plugin onRequest hook ──
445
+ // Dynamic import cached by Node.js after first call — minimal overhead
446
+ const pluginGate = await runPluginOnRequestHook({
447
+ requestId: traceId,
448
+ body,
449
+ model,
450
+ provider,
451
+ apiKeyInfo,
452
+ log,
453
+ });
454
+ if (pluginGate.blocked) {
455
+ return {
456
+ success: false,
457
+ status: 403,
458
+ error: "Request blocked by plugin",
459
+ response: pluginGate.response,
460
+ };
461
+ }
462
+ if (pluginGate.body) {
463
+ body = pluginGate.body;
464
+ }
465
+ // Per-API-key device/connection tracking (port of upstream 9router#931,
466
+ // thanks @mugnimaestra). In-memory only, never blocks the request path.
467
+ if (apiKeyInfo?.id) {
468
+ trackDevice(
469
+ apiKeyInfo.id,
470
+ extractIpFromHeaders(clientRawRequest?.headers ?? null),
471
+ userAgent ?? null
472
+ );
473
+ }
474
+ const agentGoalPolicy = resolveAgentGoalPolicy(body, clientRawRequest?.headers ?? null);
475
+ if (agentGoalPolicy.detected) {
476
+ log?.debug?.(
477
+ "AGENT_GOAL",
478
+ `long-running goal mode enabled: readinessMax=${agentGoalPolicy.readinessMaxTimeoutMs}ms streamRecovery=${agentGoalPolicy.streamRecoveryEnabled}`
479
+ );
480
+ }
481
+
482
+ let effectiveServiceTier: EffectiveServiceTier = "standard";
483
+ // Codex service-tier resolvers extracted to chatCore/serviceTier.ts (#3501); bind the per-request
484
+ // provider/credentials once and delegate so the existing call sites stay byte-identical.
485
+ const resolveEffectiveServiceTier = (requestBody?: unknown): EffectiveServiceTier =>
486
+ resolveEffectiveServiceTierFor(provider, credentials?.providerSpecificData, requestBody);
487
+ const resolveReportedServiceTier = (
488
+ payload?: unknown,
489
+ maxDepth = 3
490
+ ): EffectiveServiceTier | null => resolveReportedServiceTierFor(provider, payload, maxDepth);
491
+ // Failure usage record building extracted to chatCore/failureUsage.ts (#3501); the handler keeps
492
+ // the fire-and-forget save + computes latencyMs, so the call sites stay byte-identical.
493
+ const persistFailureUsage = (statusCode: number, errorCode?: string | null) => {
494
+ saveRequestUsage(
495
+ buildFailureUsageRecord({
496
+ provider,
497
+ model,
498
+ connectionId: getCurrentConnectionId(),
499
+ apiKeyInfo,
500
+ effectiveServiceTier,
501
+ isCombo,
502
+ comboStrategy,
503
+ statusCode,
504
+ errorCode,
505
+ latencyMs: Date.now() - startTime,
506
+ endpoint: endpointPath,
507
+ })
508
+ ).catch(() => {});
509
+ };
510
+
511
+ // Key-health updater extracted to chatCore/keyHealth.ts (#3501); bind the per-request log once
512
+ // and delegate so the existing call sites stay byte-identical.
513
+ const recordKeyHealthStatus = (
514
+ status: number,
515
+ creds: Record<string, unknown> | null | undefined
516
+ ): void => recordKeyHealthStatusFor(status, creds, log);
517
+
518
+ const persistCodexQuotaState = async (headers: Record<string, string> | null, status = 0) => {
519
+ const currentConnectionId = getCurrentConnectionId();
520
+ if (provider !== "codex" || !currentConnectionId || !headers) return;
521
+
522
+ try {
523
+ const existingProviderData =
524
+ credentials?.providerSpecificData && typeof credentials.providerSpecificData === "object"
525
+ ? (credentials.providerSpecificData as Record<string, unknown>)
526
+ : {};
527
+ // Pure payload build extracted to chatCore/codexQuota.ts (#3501). Returns null when the
528
+ // response carries no quota headers (nothing to persist).
529
+ const built = buildCodexQuotaPersistence({
530
+ headers,
531
+ existingProviderData,
532
+ modelForScope: model || requestedModel || "",
533
+ status,
534
+ });
535
+ if (!built) return;
536
+
537
+ if (built.exhaustionLog) {
538
+ log?.debug?.("CODEX", built.exhaustionLog);
539
+ }
540
+
541
+ // Invalidate the preflight cache for this connection so the next
542
+ // isModelAvailable check fetches fresh quota data.
543
+ if (status === 429) {
544
+ invalidateCodexQuotaCache(currentConnectionId);
545
+ }
546
+
547
+ await updateProviderConnection(currentConnectionId, {
548
+ providerSpecificData: built.nextProviderData,
549
+ });
550
+
551
+ credentials.providerSpecificData = built.nextProviderData;
552
+ } catch (err) {
553
+ const errMessage = err instanceof Error ? err.message : String(err);
554
+ log?.debug?.("CODEX", `Failed to persist codex quota state: ${errMessage}`);
555
+ }
556
+ };
557
+
558
+ // ── Phase 9.2: Idempotency check ──
559
+ // Resolve the idempotency key once here and reuse it at the Phase 9.2 save site below,
560
+ // rather than re-deriving it. (#3821-review LEDGER-6)
561
+ const { hit: idempotencyHit, idempotencyKey } = await checkIdempotencyCache({
562
+ clientRawRequest,
563
+ provider,
564
+ model,
565
+ effectiveServiceTier,
566
+ startTime,
567
+ log,
568
+ });
569
+ if (idempotencyHit) {
570
+ return idempotencyHit;
571
+ }
572
+
573
+ // T07: Inject connectionId into credentials so executors can rotate API keys
574
+ // using providerSpecificData.extraApiKeys (API Key Round-Robin feature)
575
+ if (connectionId && credentials && !credentials.connectionId) {
576
+ credentials.connectionId = connectionId;
577
+ }
578
+
579
+ // Endpoint/format resolution extracted to chatCore/requestFormat.ts (#3501); pure derivation
580
+ // from the inbound request, destructured so every downstream use stays byte-identical.
581
+ const {
582
+ endpointPath,
583
+ sourceFormat,
584
+ isResponsesEndpoint,
585
+ nativeCodexPassthrough,
586
+ isDroidCLI,
587
+ isOpencodeClient,
588
+ copilotCompatibleReasoning,
589
+ clientResponseFormat,
590
+ } = resolveChatCoreRequestFormat({ clientRawRequest, body, provider, userAgent });
591
+
592
+ // Check for bypass patterns (warmup, skip) - return fake response
593
+ const bypassResponse = handleBypassRequest(body, model, userAgent);
594
+ if (bypassResponse) {
595
+ return bypassResponse;
596
+ }
597
+
598
+ // ── Claude Code auto-mode classifier compat (opt-in, default "off") ──
599
+ // Claude Code's `--permission-mode auto` sends an internal classifier request that
600
+ // requires the response to START with `<block>no</block>`/`<block>yes</block>`.
601
+ // When a combo/fallback route sends that call to a cheap model returning 200 with
602
+ // empty content, Claude Code fails closed on every gated action. Detect the
603
+ // classifier request and short-circuit with a synthetic ALLOW response, WITHOUT
604
+ // calling the upstream provider. See chatCore/claudeClassifierCompat.ts.
605
+ {
606
+ const classifierSettings = cachedSettings ?? (await getCachedSettings());
607
+ if (
608
+ shouldDefaultAllowClassifier(
609
+ sourceFormat,
610
+ body as Record<string, unknown>,
611
+ classifierSettings.claudeClassifierCompat as string | undefined
612
+ )
613
+ ) {
614
+ log?.warn?.(
615
+ "CHAT",
616
+ `classifier compat=${classifierSettings.claudeClassifierCompat} | short-circuit default-allow`
617
+ );
618
+ return buildDefaultAllowClaudeMessage(requestedModel);
619
+ }
620
+ }
621
+
622
+ // Detect source format and get target format
623
+ // Model-specific targetFormat takes priority over provider default
624
+
625
+ // ── Background Task Redirection (T41) — decision extracted to chatCore/backgroundRedirect.ts (#3501)
626
+ // backgroundReason is the detection signal (threaded into memory/skills injection below); redirect
627
+ // is the actual model downgrade to apply, if any.
628
+ const { backgroundReason, redirect: bgRedirect } = resolveBackgroundTaskRedirect({
629
+ body,
630
+ headers: clientRawRequest?.headers,
631
+ model,
632
+ });
633
+ if (bgRedirect) {
634
+ const originalModel = model;
635
+ log?.info?.(
636
+ "BACKGROUND",
637
+ `Background task redirect (${bgRedirect.reason}): ${originalModel} → ${bgRedirect.degradedModel}`
638
+ );
639
+ model = bgRedirect.degradedModel;
640
+ if (body && typeof body === "object") {
641
+ body.model = model;
642
+ }
643
+
644
+ logAuditEvent({
645
+ action: "routing.background_task_redirect",
646
+ actor: apiKeyInfo?.name || "system",
647
+ target: connectionId || provider || "chat",
648
+ details: {
649
+ original_model: originalModel,
650
+ redirected_to: bgRedirect.degradedModel,
651
+ reason: bgRedirect.reason,
652
+ },
653
+ });
654
+ }
655
+
656
+ // Apply custom model aliases (Settings → Model Aliases → Pattern→Target) before routing (#315, #472)
657
+ // Custom aliases take priority over built-in and must be resolved here so the
658
+ // downstream getModelTargetFormat() lookup AND the actual provider request use
659
+ // the correct, aliased model ID. Without this, aliases only affect format detection.
660
+ const resolvedModel = resolveModelAlias(model);
661
+ // Use resolvedModel for all downstream operations (routing, provider requests, logging)
662
+ let effectiveModel = resolvedModel === model ? model : resolvedModel;
663
+ if (resolvedModel !== model) {
664
+ log?.info?.("ALIAS", `Model alias applied: ${model} → ${resolvedModel}`);
665
+ }
666
+
667
+ // Effort-variant model ids: the Claude / Claude-Code model picker (e.g. VS Code's
668
+ // "Effort" slider) advertises claude-...-{low,medium,high,xhigh,max}. Anthropic has
669
+ // no such model, so the suffixed id 404s upstream. Strip it back to the real base id
670
+ // (forwarded as the upstream model via finalModelToUpstream below) and surface the
671
+ // level as reasoning_effort so the OpenAI→Claude translator / Claude-Code bridge turn
672
+ // it into Claude thinking/effort config. An explicit client-supplied effort always
673
+ // wins; native Claude passthrough is left untouched (it carries its own `thinking`),
674
+ // and non-thinking base models are cleaned up later by normalizeThinkingForModel().
675
+ // Extracted to chatCore/claudeEffortVariant.ts (#3501); mutates body in place and returns the
676
+ // stripped model + an optional log line, keeping behaviour byte-identical.
677
+ {
678
+ const effortVariant = applyClaudeEffortVariant({
679
+ provider,
680
+ effectiveModel,
681
+ body,
682
+ sourceFormat,
683
+ });
684
+ effectiveModel = effortVariant.effectiveModel;
685
+ if (effortVariant.log) {
686
+ log?.info?.("PARAMS", effortVariant.log);
687
+ }
688
+ }
689
+
690
+ // Wire target-format resolution extracted to chatCore/targetFormat.ts (#3501); `alias` is reused
691
+ // downstream when stripping the alias/ prefix off the upstream model id.
692
+ const { alias, targetFormat } = resolveChatCoreTargetFormat({
693
+ provider,
694
+ resolvedModel,
695
+ apiFormat,
696
+ customModelTargetFormat,
697
+ providerSpecificData: credentials?.providerSpecificData,
698
+ });
699
+
700
+ const initialProviderRequest =
701
+ body && typeof body === "object" && !Array.isArray(body)
702
+ ? {
703
+ ...(body as Record<string, unknown>),
704
+ model:
705
+ typeof (body as Record<string, unknown>).model === "string"
706
+ ? (body as Record<string, unknown>).model
707
+ : effectiveModel,
708
+ }
709
+ : body;
710
+
711
+ // Track pending requests before slower optional enrichment (settings, logging,
712
+ // compression) so internal usage/runtime counters stay accurate even when
713
+ // upstream never returns response headers.
714
+ // Use credentials.connectionId as a fallback so that requests without an
715
+ // explicit session-level connectionId still register in the pendingRequests map.
716
+ const pendingConnId = connectionId || credentials?.connectionId || null;
717
+ const pendingRequestId =
718
+ trackPendingRequest(model, provider, pendingConnId, true, {
719
+ clientEndpoint: clientRawRequest?.endpoint || "/v1/chat/completions",
720
+ clientRequest: clientRawRequest?.body ?? body,
721
+ providerRequest: initialProviderRequest,
722
+ stage: "registered",
723
+ correlationId,
724
+ }) || generateRequestId();
725
+
726
+ // Initialize rate limit settings from persisted DB (once, lazy)
727
+ await initializeRateLimits();
728
+
729
+ const { body: bodyWithWebSearchFallback, fallback: webSearchFallbackPlan } =
730
+ prepareWebSearchFallbackBody(body as Record<string, unknown>, {
731
+ provider,
732
+ sourceFormat,
733
+ targetFormat,
734
+ nativeCodexPassthrough,
735
+ });
736
+ if (webSearchFallbackPlan.enabled) {
737
+ body = bodyWithWebSearchFallback as typeof body;
738
+ log?.info?.(
739
+ "TOOLS",
740
+ `Converted ${webSearchFallbackPlan.convertedToolCount} web_search tool(s) to OmniRoute fallback for ${provider}`
741
+ );
742
+ }
743
+ const noLogEnabled = apiKeyInfo?.noLog === true;
744
+ // Consolidate settings reads — fetch once, reuse throughout the request
745
+ const settings = cachedSettings ?? (await getCachedSettings());
746
+ // Opt-in tool-source diagnostics (#1825): summarize the request's tool definitions
747
+ // (count + MCP/hosted/client source breakdown + first names) as a single debug line.
748
+ if (settings.logToolSources === true) {
749
+ const toolSummary = summarizeToolSources((body as { tools?: unknown }).tools);
750
+ if (toolSummary) log?.debug?.("TOOLS", toolSummary);
751
+ }
752
+ // #1311 (opt-in): echo the client-requested alias/combo name in the response `model`
753
+ // field instead of the upstream model, so strict clients (Claude Desktop) that validate
754
+ // response.model === request.model stop rejecting alias/combo requests with a 401.
755
+ const echoModel =
756
+ settings.echoRequestedModelName === true && typeof requestedModel === "string" && requestedModel
757
+ ? requestedModel
758
+ : null;
759
+ const detailedLoggingEnabled =
760
+ !noLogEnabled &&
761
+ (settings.call_log_pipeline_enabled === true ||
762
+ settings.call_log_pipeline_enabled === "1" ||
763
+ settings.call_log_pipeline_enabled === "true");
764
+ const capturePipelineStreamChunks =
765
+ detailedLoggingEnabled && getCallLogPipelineCaptureStreamChunks();
766
+ const skillRequestId = generateRequestId();
767
+ let compressionAnalyticsWritePromise: Promise<void> | null = null;
768
+ // Compression usage-receipt attachment extracted to chatCore/compressionUsageReceipt.ts (#3501);
769
+ // pass the in-flight analytics write + request id so behaviour stays byte-identical.
770
+ const attachCompressionUsageReceiptAfterAnalytics = (
771
+ usage: Record<string, unknown>,
772
+ source: "provider" | "estimated" | "stream"
773
+ ) =>
774
+ attachCompressionUsageReceiptAfterAnalyticsFor(usage, source, {
775
+ pendingWrite: compressionAnalyticsWritePromise,
776
+ skillRequestId,
777
+ });
778
+ const pipelineSessionId =
779
+ (clientRawRequest?.headers && typeof clientRawRequest.headers.get === "function"
780
+ ? clientRawRequest.headers.get("x-omniroute-session-id")
781
+ : getHeaderValueCaseInsensitive(
782
+ clientRawRequest?.headers ?? null,
783
+ "x-omniroute-session-id"
784
+ )) || skillRequestId;
785
+ // persistAttemptLogs extracted to chatCore/attemptLogging.ts (#3501); bind the per-request context
786
+ // once so the 16 call sites keep passing only the per-attempt args (byte-identical).
787
+ const persistAttemptLogs = (args: PersistAttemptLogsArgs) =>
788
+ persistAttemptLogsFor(args, {
789
+ provider,
790
+ connectionId,
791
+ model,
792
+ skillRequestId,
793
+ detailedLoggingEnabled,
794
+ reqLogger,
795
+ pendingRequestId,
796
+ clientRawRequest,
797
+ requestedModel,
798
+ credentials,
799
+ startTime,
800
+ body,
801
+ sourceFormat,
802
+ targetFormat,
803
+ comboName,
804
+ comboStepId,
805
+ comboExecutionKey,
806
+ tokensCompressed,
807
+ apiKeyInfo,
808
+ noLogEnabled,
809
+ correlationId,
810
+ modelPinned,
811
+ });
812
+
813
+ // Primary path: merge client model id + alias target so config on either key applies; resolved
814
+ // id wins on same header name. T5 family fallback uses only (nextModel, resolveModelAlias(next))
815
+ // so A-model headers are not sent to B — see buildUpstreamHeadersForExecute.
816
+ const connectionCustomUserAgent =
817
+ credentials?.providerSpecificData &&
818
+ typeof credentials.providerSpecificData === "object" &&
819
+ typeof credentials.providerSpecificData.customUserAgent === "string"
820
+ ? credentials.providerSpecificData.customUserAgent.trim()
821
+ : "";
822
+
823
+ // Upstream extra-header building extracted to chatCore/upstreamExecuteHeaders.ts (#3501); bind the
824
+ // per-request inputs once and delegate so the existing call sites stay byte-identical.
825
+ const buildUpstreamHeadersForExecute = (modelToCall: string): Record<string, string> =>
826
+ buildUpstreamHeadersForExecuteFor({
827
+ modelToCall,
828
+ effectiveModel,
829
+ provider,
830
+ model,
831
+ resolvedModel,
832
+ sourceFormat,
833
+ connectionCustomUserAgent,
834
+ settings,
835
+ });
836
+
837
+ // Default to false unless client explicitly sets stream: true (OpenAI spec compliant)
838
+ const acceptHeader =
839
+ clientRawRequest?.headers && typeof clientRawRequest.headers.get === "function"
840
+ ? clientRawRequest.headers.get("accept") || clientRawRequest.headers.get("Accept")
841
+ : clientRawRequest?.headers?.["accept"] || clientRawRequest?.headers?.["Accept"];
842
+ const streamUserAgent = [
843
+ typeof userAgent === "string" ? userAgent : "",
844
+ getHeaderValueCaseInsensitive(clientRawRequest?.headers ?? null, "user-agent") || "",
845
+ ]
846
+ .filter(Boolean)
847
+ .join(" ");
848
+
849
+ // Explicit per-request opt-in/out for the `</think>` close marker
850
+ // (#5312 / #5245): `x-omniroute-thinking-marker: off` suppresses it for
851
+ // reasoning_content-native clients (e.g. Cursor's OpenAI path) that the UA
852
+ // allowlist does not cover; absent the header, the UA policy applies.
853
+ const thinkingMarkerHeader = getHeaderValueCaseInsensitive(
854
+ clientRawRequest?.headers ?? null,
855
+ THINKING_MARKER_HEADER
856
+ );
857
+
858
+ const explicitStreamAlias = resolveExplicitStreamAlias(body);
859
+
860
+ // Remove non-standard non-stream aliases before provider translation/execution.
861
+ // They are accepted for compatibility at the OmniRoute API boundary only.
862
+ if (body && typeof body === "object") {
863
+ const b = body as Record<string, unknown>;
864
+ if (explicitStreamAlias !== undefined) {
865
+ b.stream = explicitStreamAlias;
866
+ }
867
+
868
+ delete b.non_stream;
869
+ delete b.disable_stream;
870
+ delete b.disable_streaming;
871
+ delete b.streaming;
872
+ }
873
+
874
+ // Codex /responses/compact is JSON-only: Codex CLI does not send stream=false,
875
+ // so route shape must override the usual Accept/header fallback.
876
+ // sourceFormat="claude" applies the Anthropic Messages spec default (stream=false
877
+ // when body omits stream), preventing STREAM_EARLY_EOF on /v1/messages when
878
+ // clients send Accept: */* without an explicit stream flag.
879
+ // providerRequiresStreaming: providers with forceStream:true (cline/clinepass)
880
+ // only implement upstream streaming — a non-streaming request returns
881
+ // "generateText is not implemented" / an empty body. This flag forces the
882
+ // UPSTREAM request to stream (see `upstreamStream` below), but it MUST NOT
883
+ // force the client-facing `stream` flag: a stream:false client (e.g. the
884
+ // model-test button, plain JSON API callers) still expects a JSON response.
885
+ // The client-side `if (!stream)` branch drains the forced upstream SSE and
886
+ // converts it back to JSON via readNonStreamingResponseBody. Passing this
887
+ // flag into resolveStreamFlag would force `stream=true` and skip that
888
+ // conversion, yielding STREAM_EARLY_EOF for JSON callers. (#2081, #6126)
889
+ const providerRequiresStreaming = REGISTRY[provider]?.forceStream === true;
890
+ const stream =
891
+ nativeCodexPassthrough && isCompactResponsesEndpoint(endpointPath)
892
+ ? false
893
+ : resolveStreamFlag(body?.stream, acceptHeader, sourceFormat, {
894
+ userAgent: streamUserAgent,
895
+ streamDefaultMode: apiKeyInfo?.streamDefaultMode,
896
+ });
897
+
898
+ // `settings` is already consolidated once near the top of handleChatCore
899
+ // (the "fetch once, reuse" const). A second `const settings` here was a
900
+ // duplicate same-scope declaration that broke the esbuild/tsx transform
901
+ // ("settings has already been declared") and the production build. Reuse it.
902
+ credentials = applyCodexGlobalFastServiceTier(provider, credentials, settings, {
903
+ model: requestedModel,
904
+ body: body && typeof body === "object" ? (body as Record<string, unknown>) : null,
905
+ });
906
+ effectiveServiceTier = resolveEffectiveServiceTier(body);
907
+ setGeminiThoughtSignatureMode(settings.antigravitySignatureCacheMode);
908
+ const semanticCacheEnabled = settings.semanticCacheEnabled !== false;
909
+
910
+ const reqLogger = await createRequestLogger(sourceFormat, targetFormat, model, {
911
+ enabled: detailedLoggingEnabled,
912
+ captureStreamChunks: capturePipelineStreamChunks,
913
+ maxStreamChunkBytes: getCallLogPipelineMaxSizeBytes(),
914
+ requestId: pendingRequestId,
915
+ model,
916
+ provider: provider || undefined,
917
+ connectionId: connectionId || credentials?.connectionId || undefined,
918
+ });
919
+ const pendingScope = { id: pendingRequestId, model, provider, connectionId: pendingConnId };
920
+ const providerRequestCapture = createPreparedRequestLogger(reqLogger, pendingScope);
921
+ // 0. Log client raw request (before format conversion)
922
+ if (clientRawRequest) {
923
+ reqLogger.logClientRawRequest(
924
+ clientRawRequest.endpoint,
925
+ clientRawRequest.body,
926
+ clientRawRequest.headers
927
+ );
928
+ }
929
+
930
+ log?.debug?.("FORMAT", `${sourceFormat} → ${targetFormat} | stream=${stream}`);
931
+
932
+ // ── Phase 9.1: Semantic cache check (temp=0, any streaming mode) ──
933
+ const cacheHit = await checkSemanticCache({
934
+ semanticCacheEnabled,
935
+ body,
936
+ clientRawRequest,
937
+ model,
938
+ provider,
939
+ stream: !!stream,
940
+ reqLogger,
941
+ effectiveServiceTier,
942
+ connectionId,
943
+ startTime,
944
+ log,
945
+ persistAttemptLogs,
946
+ apiKeyId: apiKeyInfo?.id ?? undefined,
947
+ });
948
+ if (cacheHit) {
949
+ return cacheHit;
950
+ }
951
+
952
+ body = sanitizeChatRequestBody(body, sourceFormat, targetFormat);
953
+ // Per-request opt-out: clients that manage their own context send
954
+ // `x-omniroute-no-memory: true` to skip memory+skills injection (a null owner
955
+ // disables both branches in injectMemoryAndSkills). See PRD-2026-06-19-no-memory-header.
956
+ const memoryOwnerId = isNoMemoryRequested(clientRawRequest?.headers ?? null)
957
+ ? null
958
+ : resolveMemoryOwnerId(apiKeyInfo as Record<string, unknown> | null);
959
+ const injectionResult = await injectMemoryAndSkills({
960
+ body,
961
+ memoryOwnerId,
962
+ provider,
963
+ effectiveModel,
964
+ sourceFormat,
965
+ targetFormat,
966
+ backgroundReason,
967
+ log,
968
+ });
969
+ body = injectionResult.body;
970
+ const memorySettings = injectionResult.memorySettings;
971
+
972
+ // Translate request (pass reqLogger for intermediate logging)
973
+ // ── Proactive Context Compression (Phase 4) ──
974
+ // Check if context exceeds 70% of limit and compress proactively before sending to provider.
975
+ // This prevents "prompt too long" errors for large-but-not-full contexts.
976
+ const compressionBody = body
977
+ ? adaptBodyForCompression(body as Record<string, unknown>).body
978
+ : null;
979
+ const allMessages = compressionBody?.messages || body?.contents || body?.request?.contents || [];
980
+ let cavemanOutputModeApplied = false;
981
+ let cavemanOutputModeIntensity: string | null = null;
982
+ let preCompressionBody: typeof body | null = null;
983
+ let compressionResponseMeta: string | null = null;
984
+ // Delegated Context Editing (Claude only): captured at the canonical compression
985
+ // settings read below, then threaded to executor.execute() further down. Lives at
986
+ // function scope because the read happens inside the per-message compression block.
987
+ let contextEditingEnabled = false;
988
+ if (body && Array.isArray(allMessages) && allMessages.length > 0) {
989
+ let estimatedTokens = estimateTokens(allMessages);
990
+ const compressionSettingsResult = await resolveCompressionSettings(log);
991
+ const compressionSettings: CompressionConfig | null = compressionSettingsResult.settings;
992
+ const promptCompressionEnabled = compressionSettingsResult.enabled;
993
+ contextEditingEnabled = compressionSettingsResult.contextEditingEnabled;
994
+
995
+ // --- Modular Compression Pipeline (Phase 1 Lite + Phase 2 Standard/Caveman + Phase 3 Aggressive) ---
996
+ // Runs BEFORE the existing reactive compressContext() to proactively reduce tokens.
997
+ try {
998
+ const {
999
+ selectCompressionStrategy,
1000
+ selectCompressionPlan,
1001
+ enginesMapDerivesStackedPipeline,
1002
+ activeComboResolves,
1003
+ applyCompressionAsync,
1004
+ resolveCacheAwareConfig,
1005
+ formatCompressionMeta,
1006
+ buildNamedComboLookup,
1007
+ formatCompressionAnnotation,
1008
+ } = await import("../services/compression/strategySelector.ts");
1009
+ const { trackCompressionStats } = await import("../services/compression/stats.ts");
1010
+ let config: CompressionConfig = compressionSettings ?? {
1011
+ enabled: false,
1012
+ defaultMode: "off",
1013
+ autoTriggerTokens: 0,
1014
+ cacheMinutes: 5,
1015
+ preserveSystemPrompt: true,
1016
+ comboOverrides: {},
1017
+ };
1018
+ if (!promptCompressionEnabled || !compressionSettings) {
1019
+ log?.debug?.("COMPRESSION", "Prompt compression disabled or unavailable");
1020
+ }
1021
+ let compressionComboKey = comboName ?? null;
1022
+ let compressionComboApplied = false;
1023
+ const applyCompressionComboConfig = (
1024
+ compressionCombo: RuntimeCompressionCombo | null,
1025
+ routingOverrideIds: string[] = []
1026
+ ): boolean => {
1027
+ if (!compressionCombo || compressionCombo.pipeline.length === 0) return false;
1028
+ const comboLanguagePacks = [
1029
+ ...new Set(
1030
+ compressionCombo.languagePacks
1031
+ .map((pack) => pack.trim())
1032
+ .filter((pack) => pack.length > 0)
1033
+ ),
1034
+ ];
1035
+ const comboOutputIntensity = (
1036
+ ["lite", "full", "ultra"].includes(compressionCombo.outputModeIntensity)
1037
+ ? compressionCombo.outputModeIntensity
1038
+ : (config.cavemanOutputMode?.intensity ?? "full")
1039
+ ) as "lite" | "full" | "ultra";
1040
+ const comboDefaultLanguage =
1041
+ comboLanguagePacks.find((pack) => pack === config.languageConfig?.defaultLanguage) ??
1042
+ comboLanguagePacks[0] ??
1043
+ config.languageConfig?.defaultLanguage ??
1044
+ "en";
1045
+ const comboOverrides = { ...(config.comboOverrides ?? {}) };
1046
+ for (const id of routingOverrideIds) {
1047
+ if (id) comboOverrides[id] = "stacked";
1048
+ }
1049
+ config = {
1050
+ ...config,
1051
+ compressionComboId: compressionCombo.id,
1052
+ stackedPipeline: compressionCombo.pipeline,
1053
+ languageConfig: {
1054
+ ...(config.languageConfig ?? {
1055
+ enabled: false,
1056
+ defaultLanguage: "en",
1057
+ autoDetect: true,
1058
+ enabledPacks: ["en"],
1059
+ }),
1060
+ enabled: true,
1061
+ defaultLanguage: comboDefaultLanguage,
1062
+ enabledPacks:
1063
+ comboLanguagePacks.length > 0
1064
+ ? comboLanguagePacks
1065
+ : (config.languageConfig?.enabledPacks ?? ["en"]),
1066
+ },
1067
+ cavemanOutputMode: {
1068
+ ...(config.cavemanOutputMode ?? {
1069
+ enabled: false,
1070
+ intensity: "full",
1071
+ autoClarity: true,
1072
+ }),
1073
+ enabled: compressionCombo.outputMode,
1074
+ intensity: comboOutputIntensity,
1075
+ },
1076
+ comboOverrides,
1077
+ };
1078
+ compressionComboApplied = true;
1079
+ return true;
1080
+ };
1081
+ if (isCombo && comboName) {
1082
+ try {
1083
+ const { getComboByName } = await import("../../src/lib/localDb");
1084
+ let comboConfig = await getComboByName(comboName);
1085
+ if (!comboConfig && comboName.startsWith("combo/")) {
1086
+ comboConfig = await getComboByName(comboName.substring(6));
1087
+ }
1088
+ const comboRuntimeConfig =
1089
+ comboConfig?.config && typeof comboConfig.config === "object"
1090
+ ? (comboConfig.config as Record<string, unknown>)
1091
+ : {};
1092
+ const comboMode =
1093
+ typeof comboRuntimeConfig.compressionMode === "string"
1094
+ ? comboRuntimeConfig.compressionMode
1095
+ : typeof comboConfig?.compressionOverride === "string"
1096
+ ? comboConfig.compressionOverride
1097
+ : null;
1098
+ if (
1099
+ comboMode === "off" ||
1100
+ comboMode === "lite" ||
1101
+ comboMode === "standard" ||
1102
+ comboMode === "aggressive" ||
1103
+ comboMode === "ultra" ||
1104
+ comboMode === "rtk" ||
1105
+ comboMode === "stacked"
1106
+ ) {
1107
+ config = {
1108
+ ...config,
1109
+ comboOverrides: {
1110
+ ...(config.comboOverrides ?? {}),
1111
+ ...(comboName ? { [comboName]: comboMode } : {}),
1112
+ ...(comboConfig?.id ? { [String(comboConfig.id)]: comboMode } : {}),
1113
+ },
1114
+ };
1115
+ compressionComboKey = comboName;
1116
+ }
1117
+ const routingComboIds = [
1118
+ comboConfig?.id,
1119
+ comboName,
1120
+ comboName.startsWith("combo/") ? comboName.substring(6) : null,
1121
+ ].filter((id): id is string => typeof id === "string" && id.length > 0);
1122
+ if (routingComboIds.length > 0) {
1123
+ const { getCompressionComboForRoutingCombo } =
1124
+ await import("../../src/lib/db/compressionCombos.ts");
1125
+ const assignedCompressionCombo =
1126
+ routingComboIds
1127
+ .map((id) => getCompressionComboForRoutingCombo(id))
1128
+ .find((combo) => combo !== null) ?? null;
1129
+ if (
1130
+ applyCompressionComboConfig(
1131
+ assignedCompressionCombo as RuntimeCompressionCombo | null,
1132
+ routingComboIds
1133
+ )
1134
+ ) {
1135
+ compressionComboKey = comboName;
1136
+ }
1137
+ }
1138
+ } catch (err) {
1139
+ log?.debug?.(
1140
+ "COMPRESSION",
1141
+ "Combo compression override lookup skipped: " +
1142
+ (err instanceof Error ? err.message : String(err))
1143
+ );
1144
+ }
1145
+ }
1146
+ let namedCombos: Record<string, CompressionPipelineStep[]> = {};
1147
+ try {
1148
+ const { listCompressionCombos } = await import("../../src/lib/db/compressionCombos.ts");
1149
+ namedCombos = buildNamedComboLookup(listCompressionCombos());
1150
+ } catch (err) {
1151
+ log?.debug?.(
1152
+ "COMPRESSION",
1153
+ "Named combos load skipped: " + (err instanceof Error ? err.message : String(err))
1154
+ );
1155
+ }
1156
+ // Phase 3: per-request override. Unknown values fall through in the resolver (never error).
1157
+ const compressionHeader = resolveCompressionHeader(clientRawRequest?.headers ?? null);
1158
+ if (compressionHeader) {
1159
+ log?.debug?.("COMPRESSION", `x-omniroute-compression header: ${compressionHeader}`);
1160
+ }
1161
+ const modeBeforeOutputTransform = selectCompressionStrategy(
1162
+ config,
1163
+ compressionComboKey,
1164
+ estimatedTokens,
1165
+ body as Record<string, unknown>,
1166
+ { provider, targetFormat, model: effectiveModel },
1167
+ namedCombos,
1168
+ compressionHeader
1169
+ );
1170
+ if (
1171
+ modeBeforeOutputTransform === "stacked" &&
1172
+ !compressionComboApplied &&
1173
+ !config.compressionComboId &&
1174
+ isBuiltinStackedPipeline(config.stackedPipeline) &&
1175
+ // Don't let the legacy default combo override a panel-configured engines map: when the
1176
+ // operator's explicit engines derive their own stacked pipeline, that pipeline (applied
1177
+ // below from compressionPlan.stackedPipeline) is authoritative. Legacy/backfilled
1178
+ // installs (enginesExplicit false) still fall through to the seeded default combo.
1179
+ !enginesMapDerivesStackedPipeline(config) &&
1180
+ // Never let the legacy seeded default combo shadow the operator's active profile.
1181
+ !activeComboResolves(config, namedCombos)
1182
+ ) {
1183
+ try {
1184
+ const { getDefaultCompressionCombo } =
1185
+ await import("../../src/lib/db/compressionCombos.ts");
1186
+ const defaultCompressionCombo = getDefaultCompressionCombo();
1187
+ if (
1188
+ isStackedCompressionCombo(defaultCompressionCombo as RuntimeCompressionCombo | null) &&
1189
+ applyCompressionComboConfig(defaultCompressionCombo as RuntimeCompressionCombo | null)
1190
+ ) {
1191
+ log?.debug?.(
1192
+ "COMPRESSION",
1193
+ `Default compression combo applied: ${defaultCompressionCombo?.id}`
1194
+ );
1195
+ }
1196
+ } catch (err) {
1197
+ log?.debug?.(
1198
+ "COMPRESSION",
1199
+ "Default compression combo lookup skipped: " +
1200
+ (err instanceof Error ? err.message : String(err))
1201
+ );
1202
+ }
1203
+ }
1204
+ // Phase 4A: unified output styles (supersedes cavemanOutputMode via the back-compat shim).
1205
+ let outputStyleResult:
1206
+ import("../services/compression/outputStyles/apply.ts").OutputStylesResult | null = null;
1207
+ if (config.enabled) {
1208
+ try {
1209
+ const { resolveOutputStyleSelection } =
1210
+ await import("../services/compression/outputStyles/backCompat.ts");
1211
+ const selection = resolveOutputStyleSelection(config);
1212
+ if (selection.length > 0) {
1213
+ const { applyOutputStyles } =
1214
+ await import("../services/compression/outputStyles/apply.ts");
1215
+ const outputStyleLanguage =
1216
+ config.languageConfig?.enabled === true
1217
+ ? config.languageConfig.defaultLanguage
1218
+ : "en";
1219
+ outputStyleResult = applyOutputStyles(
1220
+ body as Parameters<typeof applyOutputStyles>[0],
1221
+ selection,
1222
+ outputStyleLanguage
1223
+ );
1224
+ if (outputStyleResult.applied) {
1225
+ body = outputStyleResult.body as typeof body;
1226
+ cavemanOutputModeApplied = true;
1227
+ cavemanOutputModeIntensity =
1228
+ outputStyleResult.appliedStyles?.map((s) => `${s.id}:${s.level}`).join(",") ?? null;
1229
+ estimatedTokens = estimateTokens(body?.messages ?? body?.input ?? []);
1230
+ log?.debug?.("COMPRESSION", "Output styles applied");
1231
+ } else if (
1232
+ outputStyleResult.skippedReason &&
1233
+ outputStyleResult.skippedReason !== "no_styles"
1234
+ ) {
1235
+ log?.debug?.(
1236
+ "COMPRESSION",
1237
+ `Output styles skipped: ${outputStyleResult.skippedReason}`
1238
+ );
1239
+ }
1240
+ }
1241
+ } catch (err) {
1242
+ log?.debug?.(
1243
+ "COMPRESSION",
1244
+ "Output styles skipped: " + (err instanceof Error ? err.message : String(err))
1245
+ );
1246
+ }
1247
+ }
1248
+ const compressionInputBody = body as Record<string, unknown>;
1249
+ // Adaptive context-budget (Sub-project C): model context window + request max_tokens drive
1250
+ // the budget target. getTokenLimit is already imported; provider/effectiveModel resolved above.
1251
+ const adaptiveModelContextLimit =
1252
+ provider && effectiveModel ? getTokenLimit(provider, effectiveModel) : null;
1253
+ const requestMaxTokens =
1254
+ typeof (compressionInputBody as Record<string, unknown>)?.max_tokens === "number"
1255
+ ? ((compressionInputBody as Record<string, unknown>).max_tokens as number)
1256
+ : null;
1257
+ let adaptiveTelemetry:
1258
+ import("../services/compression/adaptiveCompression/types.ts").AdaptiveTelemetry | null =
1259
+ null;
1260
+ const compressionPlan = selectCompressionPlan(
1261
+ config,
1262
+ compressionComboKey,
1263
+ estimatedTokens,
1264
+ compressionInputBody,
1265
+ { provider, targetFormat, model: effectiveModel },
1266
+ namedCombos,
1267
+ compressionHeader,
1268
+ {
1269
+ modelContextLimit: adaptiveModelContextLimit,
1270
+ requestMaxTokens: requestMaxTokens,
1271
+ onAdaptive: (t) => {
1272
+ adaptiveTelemetry = t;
1273
+ },
1274
+ }
1275
+ );
1276
+ const mode = compressionPlan.mode as CompressionConfig["defaultMode"];
1277
+ if (adaptiveTelemetry && adaptiveTelemetry.fit === false) {
1278
+ log?.warn?.(
1279
+ "COMPRESSION",
1280
+ `adaptive budget-exceeded: target=${adaptiveTelemetry.target} headroomAfter=${adaptiveTelemetry.headroomAfter} stages=${adaptiveTelemetry.stagesApplied.join(",")} (best-effort plan sent, content preserved)`
1281
+ );
1282
+ }
1283
+ compressionResponseMeta = formatCompressionMeta(compressionPlan);
1284
+ // When the per-engine toggle map derives a stacked pipeline (and no named/routing
1285
+ // combo already set config.stackedPipeline), feed that derived pipeline through so
1286
+ // applyCompressionAsync (which reads config.stackedPipeline for stacked mode) runs the
1287
+ // engines the operator actually toggled on instead of the built-in rtk+caveman default.
1288
+ if (
1289
+ mode === "stacked" &&
1290
+ compressionPlan.stackedPipeline.length > 0 &&
1291
+ !compressionComboApplied &&
1292
+ !config.compressionComboId
1293
+ ) {
1294
+ config = {
1295
+ ...config,
1296
+ stackedPipeline: compressionPlan.stackedPipeline as CompressionConfig["stackedPipeline"],
1297
+ };
1298
+ }
1299
+ let compressionAnalyticsRecorded = false;
1300
+ if (mode !== "off") {
1301
+ // #3890: in a caching context, never compress the system prompt (cacheable prefix)
1302
+ // even if the operator disabled preserveSystemPrompt — honors the cache-aware flag
1303
+ // that selectCompressionStrategy can only partially apply via the mode string.
1304
+ const cacheCtx = { provider, targetFormat, model: effectiveModel };
1305
+ const compressionConfig = resolveCacheAwareConfig(config, compressionInputBody, cacheCtx);
1306
+ const result = await applyCompressionAsync(compressionInputBody, mode, {
1307
+ model: effectiveModel,
1308
+ config: compressionConfig,
1309
+ cachingContext: cacheCtx,
1310
+ principalId: apiKeyInfo?.id ? String(apiKeyInfo.id) : undefined,
1311
+ // F3.3: stream per-engine progress live (best-effort) before compression.completed.
1312
+ onEngineStep: (s) => {
1313
+ try {
1314
+ const stepPayload = {
1315
+ requestId: traceId,
1316
+ comboId: null,
1317
+ mode,
1318
+ stepIndex: s.stepIndex,
1319
+ totalSteps: s.totalSteps,
1320
+ engine: s.engine,
1321
+ state: s.state,
1322
+ originalTokens: s.originalTokens,
1323
+ compressedTokens: s.compressedTokens,
1324
+ savingsPercent: s.savingsPercent,
1325
+ ...(s.durationMs !== undefined ? { durationMs: s.durationMs } : {}),
1326
+ timestamp: Date.now(),
1327
+ };
1328
+ emit("compression.step", stepPayload);
1329
+ void forwardDashboardEventToLiveWs("compression.step", stepPayload);
1330
+ } catch (_stepErr) {
1331
+ // best-effort live event — never fail the request
1332
+ }
1333
+ },
1334
+ });
1335
+ if (result.stats) {
1336
+ const annotation = formatCompressionAnnotation(result.stats);
1337
+ if (annotation) {
1338
+ compressionResponseMeta = `${compressionResponseMeta}; ${annotation}`;
1339
+ }
1340
+ if (result.compressed) {
1341
+ body = result.body as typeof body;
1342
+ estimatedTokens = result.stats.compressedTokens;
1343
+ tokensCompressed = Math.max(
1344
+ 0,
1345
+ result.stats.originalTokens - result.stats.compressedTokens
1346
+ );
1347
+ }
1348
+
1349
+ // Fire-and-forget: emit live compression event for dashboard (U5).
1350
+ // Guard: only emit when compression actually ran and produced stats.
1351
+ if (result.compressed && result.stats) {
1352
+ try {
1353
+ const compressionCompletedPayload = {
1354
+ requestId: traceId,
1355
+ comboId: result.stats.compressionComboId ?? null,
1356
+ mode,
1357
+ originalTokens: result.stats.originalTokens,
1358
+ compressedTokens: result.stats.compressedTokens,
1359
+ savingsPercent: result.stats.savingsPercent,
1360
+ // Single-engine modes leave engineBreakdown empty; synthesize a 1-entry
1361
+ // breakdown so the studio shows a real engine node instead of an empty pipeline.
1362
+ engineBreakdown: ensureEngineBreakdown(result.stats),
1363
+ validationWarnings: result.stats.validationWarnings,
1364
+ fallbackApplied: result.stats.fallbackApplied,
1365
+ ...(adaptiveTelemetry ? { adaptive: adaptiveTelemetry } : {}),
1366
+ timestamp: Date.now(),
1367
+ };
1368
+ emit("compression.completed", compressionCompletedPayload);
1369
+ void forwardDashboardEventToLiveWs(
1370
+ "compression.completed",
1371
+ compressionCompletedPayload
1372
+ );
1373
+ } catch (_emitErr) {
1374
+ // never propagate into the hot path — but log like the sibling
1375
+ // fire-and-forget blocks so a throwing event bus isn't fully silent.
1376
+ log?.debug?.(
1377
+ "COMPRESSION",
1378
+ "compression.completed emit skipped: " +
1379
+ (_emitErr instanceof Error ? _emitErr.message : String(_emitErr))
1380
+ );
1381
+ }
1382
+ }
1383
+
1384
+ if (result.compressed || result.stats.fallbackApplied || cavemanOutputModeApplied) {
1385
+ trackCompressionStats(result.stats);
1386
+ compressionAnalyticsRecorded = true;
1387
+ compressionAnalyticsWritePromise = writeCompressionAnalytics({
1388
+ stats: result.stats,
1389
+ provider,
1390
+ effectiveModel,
1391
+ effectiveServiceTier,
1392
+ comboName,
1393
+ mode,
1394
+ compressionComboId: config.compressionComboId,
1395
+ skillRequestId,
1396
+ cavemanOutputModeApplied,
1397
+ cavemanOutputModeIntensity,
1398
+ log,
1399
+ });
1400
+ } else {
1401
+ // Compression was attempted (mode active, engines ran) but produced no
1402
+ // recordable saving — e.g. a Stacked RTK→Caveman pipeline on already-compact
1403
+ // context. Record a skip row so analytics can distinguish "ran but saved
1404
+ // nothing" from "never ran" instead of dropping it silently (#4268).
1405
+ compressionAnalyticsRecorded = true;
1406
+ compressionAnalyticsWritePromise = writeCompressionSkip(
1407
+ {
1408
+ stats: result.stats,
1409
+ provider,
1410
+ effectiveModel,
1411
+ effectiveServiceTier,
1412
+ comboName,
1413
+ mode,
1414
+ compressionComboId: config.compressionComboId,
1415
+ skillRequestId,
1416
+ cavemanOutputModeApplied,
1417
+ cavemanOutputModeIntensity,
1418
+ log,
1419
+ },
1420
+ "no_savings"
1421
+ );
1422
+ }
1423
+
1424
+ if (result.compressed) {
1425
+ recordCompressionCacheStats({
1426
+ compressionInputBody,
1427
+ provider,
1428
+ targetFormat,
1429
+ effectiveModel,
1430
+ mode,
1431
+ stats: result.stats,
1432
+ log,
1433
+ });
1434
+ log?.info?.(
1435
+ "COMPRESSION",
1436
+ `Prompt compressed (${mode}): ${result.stats.originalTokens} -> ${result.stats.compressedTokens} tokens (${result.stats.savingsPercent}% saved, techniques: ${result.stats.techniquesUsed.join(",")})`
1437
+ );
1438
+ }
1439
+ }
1440
+ }
1441
+ if (cavemanOutputModeApplied && !compressionAnalyticsRecorded) {
1442
+ compressionAnalyticsWritePromise = writeCavemanOutputAnalytics({
1443
+ comboName,
1444
+ provider,
1445
+ compressionComboId: config.compressionComboId,
1446
+ estimatedTokens,
1447
+ skillRequestId,
1448
+ cavemanOutputModeIntensity,
1449
+ log,
1450
+ });
1451
+ }
1452
+ emitOutputStyleTelemetry({
1453
+ outputStyleResult,
1454
+ skillRequestId,
1455
+ traceId,
1456
+ effectiveModel,
1457
+ provider,
1458
+ compressionComboId: config.compressionComboId,
1459
+ estimatedTokens,
1460
+ log,
1461
+ });
1462
+ } catch (err) {
1463
+ log?.warn?.(
1464
+ "COMPRESSION",
1465
+ "Compression pipeline error (non-fatal): " +
1466
+ (err instanceof Error ? err.message : String(err))
1467
+ );
1468
+ }
1469
+ // --- End Modular Compression Pipeline ---
1470
+
1471
+ if (!promptCompressionEnabled) {
1472
+ log?.debug?.(
1473
+ "CONTEXT",
1474
+ "Skipping proactive context compression: Prompt Compression disabled"
1475
+ );
1476
+ }
1477
+ let contextLimit = getTokenLimit(provider, effectiveModel);
1478
+
1479
+ if (isCombo && comboName) {
1480
+ log?.info?.("CONTEXT", `Attempting to resolve combo limits for comboName=${comboName}`);
1481
+ try {
1482
+ const { getComboByName } = await import("../../src/lib/localDb");
1483
+ const { parseModel } = await import("../services/model.ts");
1484
+ const { resolveComboTargets } = await import("../services/combo.ts");
1485
+ let comboConfig = await getComboByName(comboName);
1486
+ if (!comboConfig && comboName.startsWith("combo/")) {
1487
+ comboConfig = await getComboByName(comboName.substring(6));
1488
+ }
1489
+ let comboTargetLimits: number[] = [];
1490
+ if (comboConfig) {
1491
+ const allCombosData = await getCombosCached();
1492
+ const targets = resolveComboTargets(
1493
+ comboConfig as unknown as { name: string; models: unknown[] },
1494
+ allCombosData as unknown as { name: string; models: unknown[] }[]
1495
+ );
1496
+ comboTargetLimits = targets.map((t: { modelStr?: string }) => {
1497
+ const parsed = parseModel(t.modelStr);
1498
+ return getTokenLimit(parsed.provider, parsed.model);
1499
+ });
1500
+ }
1501
+ // chatCore executes per concrete target (handleSingleModel resolves
1502
+ // provider/effectiveModel before delegating). Compress against THIS
1503
+ // target's window; min(...allTargets) is only a defensive fallback —
1504
+ // the old unconditional min compressed a 1M-target request at the
1505
+ // smallest sibling's window ("agent keeps forgetting things").
1506
+ const resolved = resolveComboContextLimit({
1507
+ provider,
1508
+ model: effectiveModel,
1509
+ comboTargetLimits,
1510
+ });
1511
+ contextLimit = resolved.limit;
1512
+ log?.info?.(
1513
+ "CONTEXT",
1514
+ `Combo context limit: ${resolved.limit} (source=${resolved.source})`
1515
+ );
1516
+ } catch (err) {
1517
+ log?.warn?.("CONTEXT", "Failed to resolve combo limits for compression: " + err);
1518
+ }
1519
+ }
1520
+
1521
+ const COMPRESSION_THRESHOLD = 0.7;
1522
+ let reservedTokens = 0;
1523
+ if (Array.isArray(body.tools)) {
1524
+ reservedTokens = estimateTokens(body.tools);
1525
+ }
1526
+ const threshold = Math.max(
1527
+ 1,
1528
+ Math.floor((Math.max(1, contextLimit) - reservedTokens) * COMPRESSION_THRESHOLD)
1529
+ );
1530
+
1531
+ log?.debug?.(
1532
+ "CONTEXT",
1533
+ `Checking compression: ${estimatedTokens} tokens vs ${threshold} threshold (${contextLimit} limit, ${reservedTokens} reserved)`
1534
+ );
1535
+
1536
+ // Capture pre-compression body so translators can access original message
1537
+ // content even after compression alters it (e.g. stable Kiro conversationId).
1538
+ preCompressionBody = body;
1539
+
1540
+ if (promptCompressionEnabled && estimatedTokens > threshold) {
1541
+ log?.info?.(
1542
+ "CONTEXT",
1543
+ `Proactive compression triggered: ${estimatedTokens} tokens > ${threshold} threshold (${contextLimit} limit)`
1544
+ );
1545
+
1546
+ const compressionResult = compressContext(body, {
1547
+ provider,
1548
+ model: effectiveModel,
1549
+ maxTokens: threshold,
1550
+ reserveTokens: 0,
1551
+ });
1552
+
1553
+ if (compressionResult.compressed) {
1554
+ body = compressionResult.body;
1555
+ const stats = compressionResult.stats;
1556
+ tokensCompressed = Math.max(0, (stats?.original ?? 0) - (stats?.final ?? 0));
1557
+ const layersInfo =
1558
+ stats && "layers" in stats && Array.isArray(stats.layers)
1559
+ ? ` (layers: ${stats.layers.map((l: { name: string }) => l.name).join(", ")})`
1560
+ : "";
1561
+
1562
+ log?.info?.(
1563
+ "CONTEXT",
1564
+ `Context compressed: ${stats.original} → ${stats.final} tokens${layersInfo}`
1565
+ );
1566
+
1567
+ logAuditEvent({
1568
+ action: "context.proactive_compression",
1569
+ actor: apiKeyInfo?.name || "system",
1570
+ target: connectionId || provider || "chat",
1571
+ details: {
1572
+ provider,
1573
+ model: effectiveModel,
1574
+ original_tokens: stats.original,
1575
+ final_tokens: stats.final,
1576
+ layers: "layers" in stats ? stats.layers : undefined,
1577
+ },
1578
+ });
1579
+ } else {
1580
+ log?.debug?.("CONTEXT", `Compression not applied: context already fits within target`);
1581
+ }
1582
+ }
1583
+ } else {
1584
+ log?.debug?.(
1585
+ "CONTEXT",
1586
+ `Skipping compression check: body=${!!body}, hasMessages=${Array.isArray(allMessages)}`
1587
+ );
1588
+ }
1589
+
1590
+ let translatedBody = body;
1591
+ const isClaudePassthrough = sourceFormat === FORMATS.CLAUDE && targetFormat === FORMATS.CLAUDE;
1592
+ const isClaudeCodeCompatible = isClaudeCodeCompatibleProvider(provider);
1593
+ const isClaudeCodeSemanticPassthrough = isClaudeCodeSemanticPassthroughRequest({
1594
+ provider,
1595
+ sourceFormat,
1596
+ targetFormat,
1597
+ headers: clientRawRequest?.headers,
1598
+ userAgent,
1599
+ });
1600
+ // `forceStream` providers (e.g. Cline / ClinePass) only implement upstream
1601
+ // streaming — a non-streaming request returns "generateText is not implemented"
1602
+ // / an empty body. Force the upstream request to stream even when the client
1603
+ // wants JSON; the non-streaming branch below accumulates the SSE and converts
1604
+ // it back to JSON (same mechanism already used for Claude-Code-compatible
1605
+ // providers via isClaudeCodeCompatible).
1606
+ const upstreamStream = stream || isClaudeCodeCompatible || providerRequiresStreaming;
1607
+ let ccSessionId: string | null = null;
1608
+ const stripTypes = getStripTypesForProviderModel(provider || "", model || "");
1609
+
1610
+ if (Array.isArray(translatedBody?.messages) && stripTypes.length > 0) {
1611
+ const stripResult = stripIncompatibleMessageContent(translatedBody.messages, stripTypes);
1612
+ if (stripResult.removedParts > 0) {
1613
+ translatedBody = {
1614
+ ...translatedBody,
1615
+ messages: stripResult.messages,
1616
+ };
1617
+ log?.warn?.(
1618
+ "CONTENT",
1619
+ `Stripped ${stripResult.removedParts} incompatible content part(s) for ${provider}/${model}`
1620
+ );
1621
+ }
1622
+ }
1623
+
1624
+ // Determine if we should preserve client-side cache_control headers
1625
+ // Fetch settings from DB to get user preference
1626
+ const cacheControlMode = await getCacheControlSettings().catch(() => "auto" as const);
1627
+ const preserveCacheControl = shouldPreserveCacheControl({
1628
+ userAgent,
1629
+ isCombo,
1630
+ comboStrategy,
1631
+ targetProvider: provider,
1632
+ targetFormat,
1633
+ settings: { alwaysPreserveClientCache: cacheControlMode },
1634
+ });
1635
+
1636
+ if (preserveCacheControl) {
1637
+ log?.debug?.(
1638
+ "CACHE",
1639
+ `Preserving client cache_control (client=${userAgent?.substring(0, 20)}, combo=${isCombo}, strategy=${comboStrategy}, provider=${provider})`
1640
+ );
1641
+ }
1642
+
1643
+ // extractSystemMessagesToBody + normalizeClaudeUpstreamMessages extracted to
1644
+ // chatCore/claudeUpstreamMessages.ts (#3501); bind `log` once so the call sites stay byte-identical.
1645
+ const normalizeClaudeUpstreamMessages = (
1646
+ payload: Record<string, unknown>,
1647
+ options?: { preserveToolResultBlocks?: boolean }
1648
+ ) => normalizeClaudeUpstreamMessagesFor(payload, options, log);
1649
+
1650
+ try {
1651
+ if (nativeCodexPassthrough) {
1652
+ translatedBody = { ...body, _nativeCodexPassthrough: true };
1653
+ log?.debug?.("FORMAT", "native codex passthrough enabled");
1654
+ } else if (isClaudeCodeCompatible) {
1655
+ let normalizedForCc = { ...body };
1656
+
1657
+ // Claude Code-compatible providers expect Anthropic Messages-shaped payloads,
1658
+ // but we extract only role/text/max_tokens/effort from an OpenAI-like view first.
1659
+ if (sourceFormat === FORMATS.CLAUDE && isClaudeCodeSemanticPassthrough) {
1660
+ log?.debug?.("FORMAT", "claude-code semantic passthrough enabled for compatible bridge");
1661
+ } else if (sourceFormat !== FORMATS.OPENAI) {
1662
+ const normalizeToolCallId = getModelNormalizeToolCallId(
1663
+ provider || "",
1664
+ model || "",
1665
+ sourceFormat
1666
+ );
1667
+ const preserveDeveloperRole = getModelPreserveOpenAIDeveloperRole(
1668
+ provider || "",
1669
+ model || "",
1670
+ sourceFormat
1671
+ );
1672
+ normalizedForCc = translateRequest(
1673
+ sourceFormat,
1674
+ FORMATS.OPENAI,
1675
+ model,
1676
+ { ...body },
1677
+ stream,
1678
+ credentials,
1679
+ provider,
1680
+ reqLogger,
1681
+ {
1682
+ normalizeToolCallId,
1683
+ preserveDeveloperRole,
1684
+ preserveCacheControl,
1685
+ copilotClient: copilotCompatibleReasoning,
1686
+ }
1687
+ );
1688
+ }
1689
+
1690
+ ccSessionId = resolveClaudeCodeCompatibleSessionId(clientRawRequest?.headers);
1691
+ const ccRequestDefaults = getClaudeCodeCompatibleRequestDefaults(
1692
+ credentials?.providerSpecificData
1693
+ );
1694
+ translatedBody = buildClaudeCodeCompatibleRequest({
1695
+ sourceBody: body,
1696
+ normalizedBody: normalizedForCc,
1697
+ claudeBody: sourceFormat === FORMATS.CLAUDE ? body : null,
1698
+ model,
1699
+ stream: upstreamStream,
1700
+ sessionId: ccSessionId,
1701
+ cwd: process.cwd(),
1702
+ now: new Date(),
1703
+ preserveCacheControl,
1704
+ preserveClaudeMessages: sourceFormat === FORMATS.CLAUDE && isClaudeCodeSemanticPassthrough,
1705
+ summarizeThinking: ccRequestDefaults.summarizeThinking === true,
1706
+ });
1707
+ log?.debug?.("FORMAT", "claude-code-compatible bridge enabled");
1708
+
1709
+ if (isClaudeCodeSemanticPassthrough) {
1710
+ // Semantic passthrough: only lift system/developer role messages
1711
+ // without converting file/document blocks, tool history, etc.
1712
+ extractSystemRoleMessages(translatedBody);
1713
+ } else {
1714
+ // Non-CC path: full normalization including content type conversion.
1715
+ normalizeClaudeUpstreamMessages(translatedBody, { preserveToolResultBlocks: true });
1716
+ }
1717
+ } else if (isClaudePassthrough) {
1718
+ // Pure passthrough: forward the body as-is without OpenAI round-trip.
1719
+ // The Claude→OpenAI→Claude double translation was lossy and corrupted
1720
+ // payloads at high context (150+ msgs, 100+ tools). Fix: #1359.
1721
+ // Claude Code sends well-formed Messages API payloads — trust them
1722
+ // regardless of combo strategy or cache_control settings.
1723
+ translatedBody = { ...body };
1724
+ translatedBody._disableToolPrefix = true;
1725
+
1726
+ // Sanitize historical thinking-block signatures for Anthropic-native Claude OAuth.
1727
+ // Only Anthropic's first-party API validates these signatures (token-bound); third-party
1728
+ // Claude-shape providers do not. See redactPassthroughThinkingSignatures + issue #2454.
1729
+ if (provider === "claude") {
1730
+ translatedBody.messages = redactPassthroughThinkingSignatures(
1731
+ translatedBody.messages,
1732
+ DEFAULT_THINKING_CLAUDE_SIGNATURE
1733
+ ) as typeof translatedBody.messages;
1734
+
1735
+ // Anthropic API rejects requests with both temperature and top_p.
1736
+ // VS Code Claude extension and similar clients send both; strip top_p.
1737
+ if (translatedBody.temperature !== undefined && translatedBody.top_p !== undefined) {
1738
+ delete translatedBody.top_p;
1739
+ }
1740
+ }
1741
+
1742
+ // Fix #2468: always extract role:"system" → top-level system.
1743
+ // The semantic passthrough correctly skips the Claude→OpenAI→Claude
1744
+ // round-trip, but even pure Claude bodies may carry system content as
1745
+ // role:"system" messages rather than the top-level system field, which
1746
+ // Anthropic's Messages API now rejects with a 400.
1747
+ if (isClaudeCodeSemanticPassthrough) {
1748
+ // Only lift system/developer messages — preserves Claude Code's
1749
+ // native payload structure (documents, tool chains, thinking, etc.)
1750
+ extractSystemRoleMessages(translatedBody);
1751
+ if (Array.isArray(translatedBody.messages)) {
1752
+ translatedBody.messages = splitMisplacedToolResults(
1753
+ translatedBody.messages as ClaudeMessage[]
1754
+ ) as typeof translatedBody.messages;
1755
+ }
1756
+ } else {
1757
+ normalizeClaudeUpstreamMessages(translatedBody, { preserveToolResultBlocks: true });
1758
+ }
1759
+
1760
+ log?.debug?.("FORMAT", `claude passthrough (preserveCache=${preserveCacheControl})`);
1761
+
1762
+ // Migrate deprecated top-level `output_format` → `output_config.format`.
1763
+ // Anthropic returns a 400 on the legacy field; some clients (e.g. ForgeCode)
1764
+ // still emit it. Preserves an existing output_config.format if present.
1765
+ if (translatedBody.output_format !== undefined) {
1766
+ const oc =
1767
+ translatedBody.output_config && typeof translatedBody.output_config === "object"
1768
+ ? (translatedBody.output_config as Record<string, unknown>)
1769
+ : {};
1770
+ if (oc.format === undefined) oc.format = translatedBody.output_format;
1771
+ translatedBody.output_config = oc;
1772
+ delete translatedBody.output_format;
1773
+ }
1774
+
1775
+ // Fix #1719: Strip output_config.format for non-Anthropic Claude-compatible providers.
1776
+ // Third-party Claude endpoints (MiniMax, DeepSeek via aggregators) reject this field
1777
+ // with 400 errors since they don't support Anthropic's structured output / json_schema.
1778
+ if (
1779
+ provider !== "claude" &&
1780
+ translatedBody.output_config &&
1781
+ typeof translatedBody.output_config === "object"
1782
+ ) {
1783
+ const oc = translatedBody.output_config as Record<string, unknown>;
1784
+ delete oc.format;
1785
+ if (Object.keys(oc).length === 0) {
1786
+ delete translatedBody.output_config;
1787
+ }
1788
+ }
1789
+ } else {
1790
+ translatedBody = { ...body };
1791
+
1792
+ // Issue #199 + #618: Always disable tool name prefix in Claude passthrough.
1793
+ // The proxy_ prefix was designed for OpenAI→Claude translation to avoid
1794
+ // conflicts with Claude OAuth tools, but in the passthrough path the tools
1795
+ // are already in Claude format. Applying the prefix turns "Bash" into
1796
+ // "proxy_Bash", which Claude rejects ("No such tool available: proxy_Bash").
1797
+ if (targetFormat === FORMATS.CLAUDE) {
1798
+ translatedBody._disableToolPrefix = true;
1799
+ normalizeClaudeUpstreamMessages(translatedBody);
1800
+ }
1801
+
1802
+ // OpenAI-compatible providers only support function tools.
1803
+ // Non-function tool types (computer, mcp, web_search, custom, etc.) are handled:
1804
+ // - tools with a name → converted to function format in-place before translation
1805
+ // - tools without a name AND without .function → dropped (unconvertible)
1806
+ // This must happen before translateRequest, which validates and throws on unknown types.
1807
+ if (provider?.startsWith("openai-compatible-") && Array.isArray(translatedBody.tools)) {
1808
+ const before = (translatedBody.tools as unknown[]).length;
1809
+ translatedBody.tools = (translatedBody.tools as Record<string, unknown>[])
1810
+ .filter((t) => !t.type || t.type === "function" || !!t.function || !!t.name)
1811
+ .map((t) => {
1812
+ if (!t.type || t.type === "function" || t.function) return t;
1813
+ // Named non-function tool: normalise to function format so the translator
1814
+ // does not throw on the unknown type.
1815
+ return {
1816
+ type: "function",
1817
+ function: {
1818
+ name: t.name,
1819
+ ...(t.description === undefined ? {} : { description: t.description }),
1820
+ ...(t.parameters !== undefined || t.input_schema !== undefined
1821
+ ? { parameters: t.parameters ?? t.input_schema ?? {} }
1822
+ : {}),
1823
+ ...(t.strict === undefined ? {} : { strict: t.strict }),
1824
+ },
1825
+ };
1826
+ });
1827
+ const dropped = before - (translatedBody.tools as unknown[]).length;
1828
+ if (dropped > 0) {
1829
+ log?.debug?.(
1830
+ "TOOLS",
1831
+ `Dropped ${dropped} unconvertible tool(s) for openai-compatible provider`
1832
+ );
1833
+ }
1834
+ }
1835
+
1836
+ const normalizeToolCallId = getModelNormalizeToolCallId(
1837
+ provider || "",
1838
+ model || "",
1839
+ sourceFormat
1840
+ );
1841
+ const preserveDeveloperRole = getModelPreserveOpenAIDeveloperRole(
1842
+ provider || "",
1843
+ model || "",
1844
+ sourceFormat
1845
+ );
1846
+ translatedBody = translateRequest(
1847
+ sourceFormat,
1848
+ targetFormat,
1849
+ model,
1850
+ translatedBody,
1851
+ stream,
1852
+ credentials,
1853
+ provider,
1854
+ reqLogger,
1855
+ {
1856
+ normalizeToolCallId,
1857
+ preserveDeveloperRole,
1858
+ preserveCacheControl,
1859
+ signatureNamespace: connectionId,
1860
+ copilotClient: copilotCompatibleReasoning,
1861
+ ...(preCompressionBody ? { preCompressionBody } : {}),
1862
+ }
1863
+ );
1864
+ }
1865
+ } catch (error) {
1866
+ // ── Plugin onError hook ──
1867
+ try {
1868
+ const { runOnError } = await import("@/lib/plugins/hooks");
1869
+ await runOnError(
1870
+ { requestId: traceId, body, model, provider, apiKeyInfo, metadata: {} },
1871
+ error instanceof Error ? error : new Error(String(error))
1872
+ );
1873
+ } catch (pluginErr) {
1874
+ log?.debug?.(
1875
+ "PLUGIN",
1876
+ `onError hook error (non-fatal): ${pluginErr instanceof Error ? pluginErr.message : String(pluginErr)}`
1877
+ );
1878
+ }
1879
+
1880
+ const parsedStatus = Number(error?.statusCode);
1881
+ const statusCode =
1882
+ Number.isInteger(parsedStatus) && parsedStatus >= 400 && parsedStatus <= 599
1883
+ ? parsedStatus
1884
+ : HTTP_STATUS.SERVER_ERROR;
1885
+ const message = error?.message || "Invalid request";
1886
+ const errorType = typeof error?.errorType === "string" ? error.errorType : null;
1887
+
1888
+ log?.warn?.("TRANSLATE", `Request translation failed: ${message}`);
1889
+
1890
+ if (errorType) {
1891
+ trackPendingRequest(model, provider, connectionId, false);
1892
+ return {
1893
+ success: false,
1894
+ status: statusCode,
1895
+ error: message,
1896
+ response: new Response(
1897
+ JSON.stringify({
1898
+ error: {
1899
+ message,
1900
+ type: errorType,
1901
+ code: errorType,
1902
+ },
1903
+ }),
1904
+ {
1905
+ status: statusCode,
1906
+ headers: {
1907
+ "Content-Type": "application/json",
1908
+ },
1909
+ }
1910
+ ),
1911
+ };
1912
+ }
1913
+
1914
+ trackPendingRequest(model, provider, connectionId, false);
1915
+ return createErrorResult(statusCode, message);
1916
+ }
1917
+
1918
+ trace("post_translation");
1919
+
1920
+ // Kiro: sanitize tool schemas before dispatch. Kiro returns 400 "Improperly
1921
+ // formed request" for unsupported JSON-Schema keywords (anyOf/$ref/if-then,
1922
+ // etc.) and tool names >64 chars. Strip those keys and hash-truncate long
1923
+ // names; merge the truncated→original nameMap into the existing
1924
+ // `_toolNameMap` so kiro-to-openai maps streamed tool-call names back (#1375).
1925
+ if (targetFormat === FORMATS.KIRO) {
1926
+ const kiroTools =
1927
+ translatedBody?.conversationState?.currentMessage?.userInputMessage?.userInputMessageContext
1928
+ ?.tools;
1929
+ if (kiroTools) {
1930
+ const { tools: sanitizedKiroTools, nameMap: kiroNameMap } = sanitizeKiroTools(kiroTools);
1931
+ translatedBody.conversationState.currentMessage.userInputMessage.userInputMessageContext.tools =
1932
+ sanitizedKiroTools;
1933
+ if (kiroNameMap.size > 0) {
1934
+ const existing =
1935
+ translatedBody._toolNameMap instanceof Map
1936
+ ? translatedBody._toolNameMap
1937
+ : new Map<string, string>();
1938
+ kiroNameMap.forEach((original, truncated) => existing.set(truncated, original));
1939
+ translatedBody._toolNameMap = existing;
1940
+ }
1941
+ }
1942
+ }
1943
+
1944
+ // Claude: strict Anthropic-compatible gateways (e.g. MiniMax) reject tool
1945
+ // definitions that omit the required `type` discriminator with HTTP 400. Default
1946
+ // a missing `type` to "custom" before dispatch, mirroring Anthropic's own
1947
+ // inference, so legacy Claude-format tool payloads survive strict gateways (#2195).
1948
+ if (targetFormat === FORMATS.CLAUDE && Array.isArray(translatedBody.tools)) {
1949
+ translatedBody.tools = defaultClaudeToolType(
1950
+ translatedBody.tools
1951
+ ) as typeof translatedBody.tools;
1952
+ }
1953
+
1954
+ // Extract toolNameMap for response translation (Claude OAuth)
1955
+ const translatedToolNameMap = translatedBody._toolNameMap;
1956
+ const nativeClaudeToolNameMap = isClaudePassthrough
1957
+ ? buildClaudePassthroughToolNameMap(body)
1958
+ : null;
1959
+ const toolNameMap =
1960
+ translatedToolNameMap instanceof Map && translatedToolNameMap.size > 0
1961
+ ? translatedToolNameMap
1962
+ : nativeClaudeToolNameMap;
1963
+ delete translatedBody._toolNameMap;
1964
+ delete translatedBody._disableToolPrefix;
1965
+
1966
+ // Update model in body — use resolved alias so the provider gets the correct model ID (#472)
1967
+ // Strip provider/alias prefix if it exactly matches the routing prefix so upstream receives the raw model name (#1261)
1968
+ let finalModelToUpstream = effectiveModel;
1969
+ // Defense-in-depth: only string-strip when effectiveModel is actually a string.
1970
+ // The API guards `model` via Zod (z.string()), but internal callers could pass a
1971
+ // non-string and a bare `.startsWith` would crash with `startsWith is not a
1972
+ // function` (same class as #2359 / #2463). Mirrors 9router's `?.startsWith?.()`.
1973
+ if (typeof finalModelToUpstream === "string") {
1974
+ if (finalModelToUpstream.startsWith(`${provider}/`)) {
1975
+ finalModelToUpstream = finalModelToUpstream.slice(provider.length + 1);
1976
+ } else if (alias && finalModelToUpstream.startsWith(`${alias}/`)) {
1977
+ finalModelToUpstream = finalModelToUpstream.slice(alias.length + 1);
1978
+ }
1979
+ }
1980
+ translatedBody.model = finalModelToUpstream;
1981
+
1982
+ // #3554: a combo/route may substitute the upstream model AFTER the client chose its
1983
+ // `thinking` value. Claude Code sends `thinking:{type:"disabled"}` for internal calls,
1984
+ // which claude-fable-5 (adaptive-only) rejects with a 400. Drop the now-invalid value
1985
+ // when the resolved target model rejects it; models that accept `disabled` are untouched.
1986
+ if (typeof finalModelToUpstream === "string") {
1987
+ translatedBody = normalizeThinkingForModel(translatedBody, finalModelToUpstream);
1988
+ // Claude Opus 4.7+/Fable 5 removed manual extended thinking: `thinking.type:"enabled"`
1989
+ // or any `thinking.budget_tokens` is a hard 400. Collapse any manual thinking that
1990
+ // reached this point (passthrough legacy shape, reasoning_effort buckets, per-model
1991
+ // defaults) to `{type:"adaptive"}` — effort stays on `output_config.effort`. Keyed on
1992
+ // the resolved upstream model, so it covers every routing mode. See claudeAdaptiveThinking.ts.
1993
+ translatedBody = normalizeClaudeAdaptiveThinking(translatedBody, finalModelToUpstream);
1994
+ // Claude Haiku rejects `thinking.type:"adaptive"` and `output_config.effort`
1995
+ // (both Sonnet 4.6 / Opus 4.5+ only). Several paths can still emit those
1996
+ // shapes on a Haiku target — native passthrough, reasoning_effort buckets,
1997
+ // per-model defaults — so collapse them to a Haiku-valid shape here, after
1998
+ // model substitution. Mirrors upstream 9router 401d93bd5. See
1999
+ // services/claudeHaikuConstraints.ts.
2000
+ translatedBody = normalizeClaudeHaikuConstraints(translatedBody, finalModelToUpstream);
2001
+ }
2002
+
2003
+ // Xiaomi MiMo controls reasoning ONLY via `thinking:{type:"enabled"|"disabled"}` and
2004
+ // rejects unknown/extra params with a strict "400 Param Incorrect". Map OmniRoute's
2005
+ // OpenAI reasoning signals onto that native shape: reduce any thinking object to
2006
+ // `{type}` and drop `reasoning_effort`/`reasoning`. See services/mimoThinking.ts.
2007
+ if (provider === "xiaomi-mimo") {
2008
+ translatedBody = normalizeMimoThinking(translatedBody);
2009
+ }
2010
+
2011
+ const previousResponseIdPolicy = applyResponsesPreviousResponseIdPolicy(translatedBody, {
2012
+ mode: settings.responsesPreviousResponseIdMode,
2013
+ sourceFormat,
2014
+ targetFormat,
2015
+ credentials,
2016
+ });
2017
+ translatedBody = previousResponseIdPolicy.body as typeof translatedBody;
2018
+
2019
+ // #1789: Prevent output_config.effort from overriding effort encoded in model name (Codex)
2020
+ if (provider === "codex" || provider?.startsWith("codex")) {
2021
+ const hasEffortSuffix = finalModelToUpstream.match(/-(low|medium|high|xhigh)$/i);
2022
+ if (
2023
+ hasEffortSuffix &&
2024
+ translatedBody.output_config &&
2025
+ typeof translatedBody.output_config === "object"
2026
+ ) {
2027
+ const oc = translatedBody.output_config as Record<string, unknown>;
2028
+ if (oc.effort) {
2029
+ log?.warn?.(
2030
+ "PARAMS",
2031
+ `Stripped output_config.effort="${oc.effort}" because model "${finalModelToUpstream}" already encodes effort`
2032
+ );
2033
+ delete oc.effort;
2034
+ if (Object.keys(oc).length === 0) {
2035
+ delete translatedBody.output_config;
2036
+ }
2037
+ }
2038
+ }
2039
+ }
2040
+
2041
+ // Strip unsupported parameters for reasoning models (o1, o3, etc.)
2042
+ const unsupported = getUnsupportedParams(provider, model);
2043
+ if (unsupported.length > 0) {
2044
+ const stripped: string[] = [];
2045
+ for (const param of unsupported) {
2046
+ if (Object.hasOwn(translatedBody, param)) {
2047
+ stripped.push(param);
2048
+ delete translatedBody[param];
2049
+ }
2050
+ }
2051
+ if (stripped.length > 0) {
2052
+ log?.warn?.("PARAMS", `Stripped unsupported params for ${model}: ${stripped.join(", ")}`);
2053
+ }
2054
+ }
2055
+
2056
+ // GPT-5 reasoning models (openai Chat Completions) reject temperature/top_p with a 400
2057
+ // whenever a reasoning effort is active, yet accept them under reasoning_effort=none (the
2058
+ // GPT-5.1+ default). A static unsupportedParams list can't express that, so strip sampling
2059
+ // conditionally here. The codex Responses path is already covered by the executor allowlist.
2060
+ translatedBody = stripGpt5SamplingWhenReasoning(
2061
+ translatedBody,
2062
+ provider,
2063
+ finalModelToUpstream,
2064
+ log
2065
+ );
2066
+
2067
+ // Rename max_tokens to max_completion_tokens if not supported (#1961)
2068
+ if (!supportsMaxTokens({ provider, model })) {
2069
+ if (translatedBody.max_tokens !== undefined) {
2070
+ if (translatedBody.max_completion_tokens === undefined) {
2071
+ translatedBody.max_completion_tokens = translatedBody.max_tokens;
2072
+ }
2073
+ delete translatedBody.max_tokens;
2074
+ log?.debug?.("PARAMS", `Renamed max_tokens to max_completion_tokens for ${model}`);
2075
+ }
2076
+ }
2077
+
2078
+ // OpenAI's `store` parameter is not supported by most compatible providers and breaks them
2079
+ if (provider !== "openai" && "store" in translatedBody) {
2080
+ delete translatedBody.store;
2081
+ }
2082
+
2083
+ // Chat clients may send stream_options.include_usage, but OpenAI Responses
2084
+ // upstreams (including Azure AI Foundry /responses) reject stream_options.
2085
+ if (targetFormat === FORMATS.OPENAI_RESPONSES && "stream_options" in translatedBody) {
2086
+ delete translatedBody.stream_options;
2087
+ }
2088
+
2089
+ // Provider-specific max_tokens caps (#711)
2090
+ // Some providers reject requests when max_tokens exceeds their API limit.
2091
+ // Cap before sending to avoid upstream HTTP 400 errors.
2092
+ const providerCap = PROVIDER_MAX_TOKENS[provider];
2093
+ if (providerCap) {
2094
+ for (const field of ["max_tokens", "max_completion_tokens"] as const) {
2095
+ if (typeof translatedBody[field] === "number" && translatedBody[field] > providerCap) {
2096
+ log?.debug?.(
2097
+ "PARAMS",
2098
+ `Capping ${field} from ${translatedBody[field]} to ${providerCap} for ${provider}`
2099
+ );
2100
+ translatedBody[field] = providerCap;
2101
+ }
2102
+ }
2103
+ }
2104
+
2105
+ // Resolve executor with optional upstream proxy (CLIProxyAPI) routing.
2106
+ // mode="native" (default): returns the native executor unchanged.
2107
+ // mode="cliproxyapi": returns the CLIProxyAPI executor instead.
2108
+ // mode="fallback": returns a wrapper that tries native first, falls back to CLIProxyAPI on 5xx/network errors.
2109
+
2110
+ // #6339: pass the resolved connection's providerSpecificData so a per-connection
2111
+ // cliproxyapiMode="claude-native" override can deep-route this single connection
2112
+ // through CLIProxyAPI regardless of the provider-level upstream_proxy_config mode.
2113
+ const resolveExecutorWithProxy = (prov: string) =>
2114
+ resolveExecutorWithProxyFor(
2115
+ prov,
2116
+ log,
2117
+ (credentials?.providerSpecificData as Record<string, unknown> | null | undefined) ?? null
2118
+ );
2119
+
2120
+ // === Quota Share enforcement PRE-hook (B/F7) ===
2121
+ // Runs after provider/model/credentials/apiKeyInfo are fully resolved,
2122
+ // before dispatcher. Fail-open per B16: errors → allow.
2123
+ let quotaSoftDeprioritize = false;
2124
+ if (apiKeyInfo?.id && credentials?.connectionId) {
2125
+ try {
2126
+ const { enforceQuotaShare } = await import("@/lib/quota/enforce");
2127
+ const decision = await enforceQuotaShare({
2128
+ apiKeyId: apiKeyInfo.id,
2129
+ connectionId: credentials.connectionId,
2130
+ provider: provider ?? "unknown",
2131
+ // Resolved model id (post background-redirect / alias) — the same scope the
2132
+ // router/log use. Operators configure per-(key,model) caps against THIS id.
2133
+ model: model || undefined,
2134
+ estimatedCost: {},
2135
+ }).catch((err: unknown) => {
2136
+ log?.warn?.(
2137
+ "QUOTA_SHARE",
2138
+ `enforceQuotaShare failed; fail-open: ${err instanceof Error ? err.message : String(err)}`
2139
+ );
2140
+ return { kind: "allow" as const };
2141
+ });
2142
+
2143
+ if (decision.kind === "block") {
2144
+ const { buildErrorBody } = await import("../utils/error.ts");
2145
+ log?.warn?.(
2146
+ "QUOTA_SHARE",
2147
+ `[quotaShare] blocked apiKeyId=${apiKeyInfo.id} provider=${provider ?? "unknown"}: ${decision.reason}`
2148
+ );
2149
+ const headers: Record<string, string> = { "Content-Type": "application/json" };
2150
+ if (decision.retryAfterSeconds) {
2151
+ headers["Retry-After"] = String(decision.retryAfterSeconds);
2152
+ }
2153
+ return new Response(JSON.stringify(buildErrorBody(429, decision.reason)), {
2154
+ status: 429,
2155
+ headers,
2156
+ });
2157
+ }
2158
+
2159
+ if (decision.kind === "allow" && decision.deprioritize) {
2160
+ quotaSoftDeprioritize = true;
2161
+ log?.info?.(
2162
+ "QUOTA_SHARE",
2163
+ `[quotaShare] soft deprioritize active for apiKeyId=${apiKeyInfo.id} provider=${provider ?? "unknown"}`
2164
+ );
2165
+ }
2166
+ } catch (err) {
2167
+ // Outer fail-open guard — should not be reached (inner .catch covers it)
2168
+ log?.warn?.(
2169
+ "QUOTA_SHARE",
2170
+ `[quotaShare] enforceQuotaShare unexpected error; fail-open: ${err instanceof Error ? err.message : String(err)}`
2171
+ );
2172
+ }
2173
+ }
2174
+ // G2: Propagate soft penalty to the current candidate so combo scoring can deprioritize.
2175
+ if (quotaSoftDeprioritize && isCombo && comboStepId) {
2176
+ try {
2177
+ const { setCandidateQuotaSoftPenalty } = await import("../services/combo");
2178
+ setCandidateQuotaSoftPenalty(comboExecutionKey, comboStepId, true);
2179
+ } catch (err) {
2180
+ log?.warn?.(
2181
+ "QUOTA_SHARE",
2182
+ `[quotaShare] could not set soft penalty on candidate: ${err instanceof Error ? err.message : String(err)}`
2183
+ );
2184
+ }
2185
+ }
2186
+ // === /Quota Share enforcement PRE-hook ===
2187
+
2188
+ // Get executor for this provider (with optional upstream proxy routing)
2189
+ const executor = await resolveExecutorWithProxy(provider);
2190
+ const getExecutionCredentials = () =>
2191
+ resolveExecutionCredentialsFor({
2192
+ credentials,
2193
+ nativeCodexPassthrough,
2194
+ endpointPath,
2195
+ targetFormat,
2196
+ provider,
2197
+ ccSessionId,
2198
+ });
2199
+
2200
+ let onPipelineStreamError: streamFailure.PipelineStreamErrorHandler | null = null;
2201
+ let onClientDisconnectFinalize:
2202
+ ((event: { reason: string; duration: number }) => boolean) | null = null;
2203
+
2204
+ // Create stream controller for disconnect detection
2205
+ const streamController = createStreamController({
2206
+ onDisconnect: (event) => {
2207
+ let finalized = false;
2208
+ try {
2209
+ finalized = onClientDisconnectFinalize?.(event) === true;
2210
+ } catch {}
2211
+ if (!finalized) {
2212
+ try {
2213
+ finalizePendingScope(pendingScope, {
2214
+ status: 499,
2215
+ error: `Client disconnected: ${event.reason}`,
2216
+ errorCode: "client_disconnected",
2217
+ });
2218
+ finalized = true;
2219
+ } catch {}
2220
+ }
2221
+ try {
2222
+ onDisconnect?.(event);
2223
+ } catch {}
2224
+ return finalized;
2225
+ },
2226
+ onError: (event) => onPipelineStreamError?.(event),
2227
+ provider,
2228
+ model,
2229
+ connectionId,
2230
+ clientResponseFormat,
2231
+ clientAbortSignal: clientRawRequest?.signal,
2232
+ });
2233
+
2234
+ const dedupRequestBody = { ...translatedBody, model: `${provider}/${model}`, stream };
2235
+ const dedupEnabled = shouldDeduplicate(dedupRequestBody);
2236
+ const dedupHash = dedupEnabled ? computeRequestHash(dedupRequestBody) : null;
2237
+
2238
+ const executeProviderRequest = async (modelToCall = effectiveModel, allowDedup = false) => {
2239
+ const execute = async () => {
2240
+ // Upstream body preparation extracted to chatCore/upstreamBody.ts (#3501 — first internal
2241
+ // sub-slice of executeProviderRequest); produces the body sent upstream (payload rules +
2242
+ // tool-limit truncation + qwen oauth user backfill + prompt_cache_key injection).
2243
+ let bodyToSend = await prepareUpstreamBody({
2244
+ translatedBody,
2245
+ modelToCall,
2246
+ provider,
2247
+ targetFormat,
2248
+ credentials,
2249
+ log,
2250
+ bypassDefaultToolLimit: isOpencodeClient,
2251
+ });
2252
+
2253
+ updatePendingScope(pendingScope, {
2254
+ providerRequest: bodyToSend,
2255
+ stage: "payload_prepared",
2256
+ });
2257
+
2258
+ let releaseRawResultAccountSemaphore = () => {};
2259
+ try {
2260
+ const rawResult = await (async () => {
2261
+ let attempts = 0;
2262
+ const isModelScopeForRequest = isModelScope();
2263
+ const maxAttempts = isModelScopeForRequest
2264
+ ? 3
2265
+ : provider === "qwen"
2266
+ ? 3
2267
+ : provider === "codex"
2268
+ ? 3
2269
+ : 1;
2270
+
2271
+ // ── Codex 429 account-rotation state ─────────────────────────────────
2272
+ // Track excluded connection IDs for codex failover across attempts.
2273
+ const codexExcludedIds: string[] = [];
2274
+ // Derive session affinity key once for codex failover (used to clear affinity on 429).
2275
+ const codexSessionAffinityKey =
2276
+ provider === "codex"
2277
+ ? (extractSessionAffinityKey(body, clientRawRequest?.headers) ?? null)
2278
+ : null;
2279
+
2280
+ while (attempts < maxAttempts) {
2281
+ trace("pre_executor", { attempt: attempts });
2282
+ updatePendingScope(pendingScope, {
2283
+ stage: "sending_to_provider",
2284
+ });
2285
+ const execCreds = getExecutionCredentials();
2286
+ const attemptConnectionId = execCreds?.connectionId || connectionId;
2287
+ const accountSemaphoreMaxConcurrency = resolveAccountSemaphoreMaxConcurrency(execCreds);
2288
+ const accountSemaphoreKey = resolveAccountSemaphoreKey({
2289
+ provider,
2290
+ model: modelToCall,
2291
+ connectionId: attemptConnectionId,
2292
+ credentials: execCreds,
2293
+ });
2294
+
2295
+ trace("pre_semaphore", {
2296
+ semaphoreKey: accountSemaphoreKey,
2297
+ max: accountSemaphoreMaxConcurrency,
2298
+ });
2299
+ if (accountSemaphoreKey && accountSemaphoreMaxConcurrency != null) {
2300
+ updatePendingScope(pendingScope, {
2301
+ stage: "waiting_account_slot",
2302
+ });
2303
+ }
2304
+ const releaseAccountSemaphore =
2305
+ accountSemaphoreKey && accountSemaphoreMaxConcurrency != null
2306
+ ? await acquireAccountSemaphore(accountSemaphoreKey, {
2307
+ maxConcurrency: accountSemaphoreMaxConcurrency,
2308
+ signal: streamController.signal,
2309
+ })
2310
+ : () => {};
2311
+ trace("post_semaphore");
2312
+ updatePendingScope(pendingScope, {
2313
+ stage: "waiting_rate_limit",
2314
+ });
2315
+
2316
+ try {
2317
+ trace("pre_rate_limit", { connectionId: attemptConnectionId });
2318
+ const rawExecutorResult = await withRateLimit(
2319
+ provider,
2320
+ attemptConnectionId,
2321
+ modelToCall,
2322
+ async () => {
2323
+ trace("inside_rate_limit", { connectionId: attemptConnectionId });
2324
+ updatePendingScope(pendingScope, {
2325
+ stage: "rate_limit_slot_acquired",
2326
+ });
2327
+ return executeWithUpstreamStartTimeout({
2328
+ executor,
2329
+ provider,
2330
+ model: modelToCall,
2331
+ signal: streamController.signal,
2332
+ log,
2333
+ execute: (signal) =>
2334
+ runWithCapture(providerRequestCapture, () =>
2335
+ executor.execute({
2336
+ model: modelToCall,
2337
+ body: bodyToSend,
2338
+ stream: upstreamStream,
2339
+ credentials: execCreds,
2340
+ signal,
2341
+ log,
2342
+ extendedContext,
2343
+ upstreamExtraHeaders: buildUpstreamHeadersForExecute(modelToCall),
2344
+ clientHeaders: buildExecutorClientHeaders(
2345
+ clientRawRequest?.headers,
2346
+ userAgent
2347
+ ),
2348
+ onCredentialsRefreshed,
2349
+ skipUpstreamRetry,
2350
+ contextEditing: { enabled: contextEditingEnabled },
2351
+ })
2352
+ ),
2353
+ });
2354
+ },
2355
+ streamController.signal
2356
+ );
2357
+ const res = normalizeExecutorResult(rawExecutorResult);
2358
+ trace("post_executor", { status: res?.response?.status });
2359
+
2360
+ // Track Gemini RPM + RPD request counts for 429 classification
2361
+ if (provider === "gemini") {
2362
+ incrementRequestCount(modelToCall);
2363
+ }
2364
+
2365
+ updatePendingScope(pendingScope, {
2366
+ stage: "provider_response_started",
2367
+ });
2368
+
2369
+ if (res.response.status === 401 && execCreds?.connectionId) {
2370
+ recordKeyHealthStatus(401, execCreds);
2371
+ }
2372
+
2373
+ // Qwen 429 strict quota backoff (wait 1.5s, 3s and retry)
2374
+ if (
2375
+ provider === "qwen" &&
2376
+ res.response.status === 429 &&
2377
+ attempts < maxAttempts - 1
2378
+ ) {
2379
+ const bodyPeek = await res.response
2380
+ .clone()
2381
+ .text()
2382
+ .catch(() => "");
2383
+ if (bodyPeek.toLowerCase().includes("exceeded your current quota")) {
2384
+ const delay = 1500 * (attempts + 1);
2385
+ log?.warn?.("QWEN_RETRY", `Quota 429 hit. Retrying in ${delay}ms...`);
2386
+ releaseAccountSemaphore();
2387
+ await new Promise((r) => setTimeout(r, delay));
2388
+ attempts++;
2389
+ continue;
2390
+ }
2391
+ }
2392
+
2393
+ if (isModelScope() && res.response.status === 429 && attempts < maxAttempts - 1) {
2394
+ const bodyPeek = await res.response
2395
+ .clone()
2396
+ .text()
2397
+ .catch(() => "");
2398
+ const normalizedHeaders = normalizeHeaders(res.response.headers);
2399
+ const decision = classifyModelScope429(bodyPeek, normalizedHeaders);
2400
+ if (decision.retryable) {
2401
+ const delay = getModelScopeRetryDelayMs(normalizedHeaders, attempts);
2402
+ log?.warn?.(
2403
+ "MODELSCOPE_RETRY",
2404
+ `429 ${decision.kind}; retrying in ${delay}ms (model remaining: ${decision.snapshot.modelRemaining ?? "unknown"})`
2405
+ );
2406
+ releaseAccountSemaphore();
2407
+ await new Promise((r) => setTimeout(r, delay));
2408
+ attempts++;
2409
+ continue;
2410
+ }
2411
+ }
2412
+
2413
+ // Codex 429 account-rotation failover (disabled for context-relay so combo.ts can inject handoff)
2414
+ if (
2415
+ provider === "codex" &&
2416
+ comboStrategy !== "context-relay" &&
2417
+ res.response.status === 429 &&
2418
+ attempts < maxAttempts - 1
2419
+ ) {
2420
+ const failedConnectionId =
2421
+ execCreds?.connectionId || credentials?.connectionId || connectionId;
2422
+ const normalizedHeaders = normalizeHeaders(res.response.headers);
2423
+ const retryAfterHeader = normalizedHeaders["retry-after"] ?? null;
2424
+ const retryAfterMs = retryAfterHeader
2425
+ ? Number.parseFloat(retryAfterHeader) * 1000
2426
+ : null;
2427
+
2428
+ log?.warn?.(
2429
+ "CODEX_FAILOVER",
2430
+ `429 on connection ${String(failedConnectionId).slice(0, 8)} (attempt ${attempts + 1}/${maxAttempts}), rotating account`
2431
+ );
2432
+
2433
+ // Mark only the current Codex model scope as rate-limited.
2434
+ if (failedConnectionId) {
2435
+ await markCodexScopeRateLimited({
2436
+ failedConnectionId: String(failedConnectionId),
2437
+ model: modelToCall || model || requestedModel || null,
2438
+ rateLimitedUntil: new Date(Date.now() + (retryAfterMs || 60_000)).toISOString(),
2439
+ credentials,
2440
+ });
2441
+ // Fix B: also persist the cooldown to
2442
+ // `provider_connections.rate_limited_until`. Without this,
2443
+ // the Codex 429 cascade survives the current request (via
2444
+ // `markCodexScopeRateLimited`'s in-memory Map) but is lost
2445
+ // on process restart — the same exhausted Codex key is
2446
+ // re-picked on the very next request. Mirrors
2447
+ // `open-sse/executors/antigravity.ts:343`.
2448
+ // Best-effort: never crash the chat path on DB write failure.
2449
+ try {
2450
+ const { setConnectionRateLimitUntil } = await import("@/lib/db/providers");
2451
+ const untilMs = Date.now() + (retryAfterMs || 60_000);
2452
+ setConnectionRateLimitUntil(String(failedConnectionId), untilMs);
2453
+ } catch {
2454
+ // ignore — best effort
2455
+ }
2456
+ if (!codexExcludedIds.includes(String(failedConnectionId))) {
2457
+ codexExcludedIds.push(String(failedConnectionId));
2458
+ }
2459
+ }
2460
+
2461
+ // Clear session affinity so next request won't be pinned to the failing account
2462
+ if (codexSessionAffinityKey) {
2463
+ try {
2464
+ deleteSessionAccountAffinity(codexSessionAffinityKey, "codex");
2465
+ } catch {
2466
+ // best-effort
2467
+ }
2468
+ }
2469
+
2470
+ // Fetch next available codex connection (excluding all previously failed ones)
2471
+ const nextCreds = await getProviderCredentials(
2472
+ "codex",
2473
+ null,
2474
+ null,
2475
+ modelToCall || model || requestedModel || null,
2476
+ {
2477
+ excludeConnectionIds: [...codexExcludedIds],
2478
+ }
2479
+ ).catch(() => null);
2480
+
2481
+ if (!nextCreds || nextCreds.allRateLimited) {
2482
+ log?.warn?.("CODEX_FAILOVER", "No more codex accounts available — returning 429");
2483
+ if (stream) {
2484
+ releaseAccountSemaphore();
2485
+ return {
2486
+ ...res,
2487
+ _executionCredentials: execCreds,
2488
+ };
2489
+ }
2490
+ return {
2491
+ ...res,
2492
+ _accountSemaphoreRelease: releaseAccountSemaphore,
2493
+ _executionCredentials: execCreds,
2494
+ };
2495
+ }
2496
+
2497
+ const newConnectionId = nextCreds.connectionId;
2498
+ log?.info?.(
2499
+ "CODEX_FAILOVER",
2500
+ `Rotating codex account: ${String(failedConnectionId).slice(0, 8)} → ${newConnectionId.slice(0, 8)} (attempt ${attempts + 2}/${maxAttempts})`
2501
+ );
2502
+
2503
+ logAuditEvent({
2504
+ action: "codex.account_rotation",
2505
+ actor: apiKeyInfo?.name || "system",
2506
+ target: newConnectionId,
2507
+ details: {
2508
+ failed_connection_id: failedConnectionId,
2509
+ new_connection_id: newConnectionId,
2510
+ attempt: attempts + 1,
2511
+ retry_after_ms: retryAfterMs,
2512
+ },
2513
+ });
2514
+
2515
+ // Update credentials in-place so getExecutionCredentials() picks up the new account
2516
+ Object.assign(credentials, nextCreds);
2517
+
2518
+ releaseAccountSemaphore();
2519
+ attempts++;
2520
+ continue;
2521
+ }
2522
+
2523
+ // For streaming: release the semaphore when the client drains or cancels the stream.
2524
+ if (stream) {
2525
+ const originalBody = res.response.body;
2526
+ if (!originalBody) {
2527
+ releaseAccountSemaphore();
2528
+ return res;
2529
+ }
2530
+
2531
+ // Opt-in transparent stream recovery (free-claude-code port, default OFF).
2532
+ // Only engages for a successful (2xx) stream — an error body must never be
2533
+ // held or replayed. Setting is read once here from the cached resolved
2534
+ // resilience settings; the default path is byte-for-byte unchanged.
2535
+ const okStatus = res.response.status >= 200 && res.response.status < 300;
2536
+ let streamRecoveryEnabled = false;
2537
+ let continueMidStreamEnabled = false;
2538
+ if (okStatus) {
2539
+ try {
2540
+ // Reuse the request-consolidated settings read (see line ~2076) — no
2541
+ // second DB/cache hit. Default OFF when the setting is absent.
2542
+ const sr = resolveResilienceSettings(settings).streamRecovery;
2543
+ // Fail-closed: the agent-goal-policy heuristic may only ADD recovery
2544
+ // when the operator has no explicit configuration. If the operator
2545
+ // explicitly configured stream recovery (env var or DB/settings
2546
+ // override), that value always wins — the goal policy must never
2547
+ // re-enable recovery the operator explicitly turned off.
2548
+ const operatorExplicit = isStreamRecoveryExplicitlyConfigured(settings);
2549
+ const goalOverride = !operatorExplicit && agentGoalPolicy.streamRecoveryEnabled;
2550
+ streamRecoveryEnabled = sr.enabled || goalOverride;
2551
+ continueMidStreamEnabled = sr.continueMidStream === true;
2552
+ if (goalOverride && !sr.enabled) {
2553
+ log?.info?.(
2554
+ "AGENT_GOAL",
2555
+ `agentGoalPolicy override: stream recovery enabled for goal request requestId=${traceId} model=${modelToCall || model || requestedModel || "unknown"}`
2556
+ );
2557
+ }
2558
+ } catch {
2559
+ streamRecoveryEnabled = false;
2560
+ continueMidStreamEnabled = false;
2561
+ }
2562
+ }
2563
+
2564
+ let clientBody: ReadableStream<Uint8Array>;
2565
+ if (streamRecoveryEnabled) {
2566
+ // Run the SAME upstream (same account/creds) with a given body and return
2567
+ // its 2xx stream, or null. Used both by the early-retry re-open (same body)
2568
+ // and the mid-stream continuation (assistant-prefilled body).
2569
+ const runUpstreamStream = async (
2570
+ body: unknown
2571
+ ): Promise<ReadableStream<Uint8Array> | null> => {
2572
+ try {
2573
+ const retryRaw = await executeWithUpstreamStartTimeout({
2574
+ executor,
2575
+ provider,
2576
+ model: modelToCall,
2577
+ signal: streamController.signal,
2578
+ log,
2579
+ execute: (signal) =>
2580
+ runWithCapture(providerRequestCapture, () =>
2581
+ executor.execute({
2582
+ model: modelToCall,
2583
+ body,
2584
+ stream: upstreamStream,
2585
+ credentials: execCreds,
2586
+ signal,
2587
+ log,
2588
+ extendedContext,
2589
+ upstreamExtraHeaders: buildUpstreamHeadersForExecute(modelToCall),
2590
+ clientHeaders: buildExecutorClientHeaders(
2591
+ clientRawRequest?.headers,
2592
+ userAgent
2593
+ ),
2594
+ onCredentialsRefreshed,
2595
+ skipUpstreamRetry,
2596
+ contextEditing: { enabled: contextEditingEnabled },
2597
+ })
2598
+ ),
2599
+ });
2600
+ const retryRes = normalizeExecutorResult(retryRaw);
2601
+ const retryOk =
2602
+ retryRes.response.status >= 200 && retryRes.response.status < 300;
2603
+ if (retryOk && retryRes.response.body) {
2604
+ return retryRes.response.body as ReadableStream<Uint8Array>;
2605
+ }
2606
+ await retryRes.response.body?.cancel().catch(() => {});
2607
+ return null;
2608
+ } catch {
2609
+ return null;
2610
+ }
2611
+ };
2612
+
2613
+ // Mid-stream continuation (Fase 4.4): re-request with the partial text as an
2614
+ // assistant prefill. Gated by its own setting and only for OpenAI-compatible
2615
+ // bodies (makeContinuationBody returns null otherwise).
2616
+ const continueStream = continueMidStreamEnabled
2617
+ ? (assistantSoFar: string) => {
2618
+ const continuationBody = makeContinuationBody(
2619
+ bodyToSend as Record<string, unknown>,
2620
+ assistantSoFar
2621
+ );
2622
+ return continuationBody
2623
+ ? runUpstreamStream(continuationBody)
2624
+ : Promise.resolve(null);
2625
+ }
2626
+ : undefined;
2627
+
2628
+ clientBody = createRecoverableStream(
2629
+ originalBody as ReadableStream<Uint8Array>,
2630
+ () => runUpstreamStream(bodyToSend),
2631
+ {
2632
+ finalize: releaseAccountSemaphore,
2633
+ onRetry: (attempt, err) =>
2634
+ log?.warn?.(
2635
+ "STREAM_RECOVERY",
2636
+ `transparent early-retry ${attempt}/${STREAM_RECOVERY.EARLY_RETRY_MAX} after ${
2637
+ (err as { name?: string })?.name || "truncation"
2638
+ }`
2639
+ ),
2640
+ continueStream,
2641
+ onContinue: (attempt) =>
2642
+ log?.warn?.(
2643
+ "STREAM_RECOVERY",
2644
+ `mid-stream continuation attempt ${attempt}/${STREAM_RECOVERY.EARLY_RETRY_MAX}`
2645
+ ),
2646
+ }
2647
+ );
2648
+ } else {
2649
+ clientBody = wrapReadableStreamWithFinalize(
2650
+ originalBody,
2651
+ releaseAccountSemaphore
2652
+ );
2653
+ }
2654
+
2655
+ return {
2656
+ ...res,
2657
+ _executionCredentials: execCreds,
2658
+ response: new Response(clientBody, {
2659
+ status: res.response.status,
2660
+ statusText: res.response.statusText,
2661
+ headers: new Headers(normalizeHeaders(res.response.headers)),
2662
+ }),
2663
+ };
2664
+ }
2665
+
2666
+ return {
2667
+ ...res,
2668
+ _executionCredentials: execCreds,
2669
+ _accountSemaphoreRelease: releaseAccountSemaphore,
2670
+ };
2671
+ } catch (error) {
2672
+ releaseAccountSemaphore();
2673
+ throw error;
2674
+ }
2675
+ }
2676
+ })();
2677
+
2678
+ if (stream) {
2679
+ return rawResult;
2680
+ }
2681
+
2682
+ // Non-stream: release semaphore immediately after reading full response body.
2683
+ const status = rawResult.response.status;
2684
+
2685
+ // Use execution credentials captured during request processing
2686
+ if (
2687
+ rawResult._executionCredentials?.connectionId &&
2688
+ rawResult._executionCredentials?.apiKey
2689
+ ) {
2690
+ recordKeyHealthStatus(status, rawResult._executionCredentials);
2691
+ }
2692
+ releaseRawResultAccountSemaphore =
2693
+ typeof rawResult._accountSemaphoreRelease === "function"
2694
+ ? rawResult._accountSemaphoreRelease
2695
+ : () => {};
2696
+
2697
+ const statusText = rawResult.response.statusText;
2698
+ const headersObj = normalizeHeaders(rawResult.response.headers);
2699
+ const responseHeaders = new Headers(headersObj);
2700
+ stripStaleForwardingHeaders(responseHeaders);
2701
+ stripNextMiddlewareControlHeaders(responseHeaders);
2702
+ const contentType = (responseHeaders.get("content-type") || "").toLowerCase();
2703
+ const payload = await readNonStreamingResponseBody(
2704
+ rawResult.response,
2705
+ contentType,
2706
+ upstreamStream
2707
+ );
2708
+ releaseRawResultAccountSemaphore();
2709
+ releaseRawResultAccountSemaphore = () => {};
2710
+
2711
+ return {
2712
+ ...rawResult,
2713
+ response: new Response(payload, { status, statusText, headers: responseHeaders }),
2714
+ _dedupSnapshot: {
2715
+ status,
2716
+ statusText,
2717
+ headers: (() => {
2718
+ const arr: [string, string][] = [];
2719
+ responseHeaders.forEach((v, k) => arr.push([k, v]));
2720
+ return arr;
2721
+ })(),
2722
+ payload,
2723
+ },
2724
+ };
2725
+ } catch (error) {
2726
+ releaseRawResultAccountSemaphore();
2727
+ throw error;
2728
+ }
2729
+ };
2730
+
2731
+ if (allowDedup && dedupEnabled && dedupHash) {
2732
+ const dedupResult = await deduplicate(dedupHash, execute);
2733
+ if (dedupResult.wasDeduplicated) {
2734
+ log?.debug?.("DEDUP", `Joined in-flight request hash=${dedupHash}`);
2735
+ }
2736
+ return materializeDeduplicatedExecutionResult(dedupResult.result);
2737
+ }
2738
+
2739
+ return execute();
2740
+ };
2741
+
2742
+ const registeredProviderRequest =
2743
+ translatedBody && typeof translatedBody === "object" && !Array.isArray(translatedBody)
2744
+ ? {
2745
+ ...(translatedBody as Record<string, unknown>),
2746
+ model:
2747
+ typeof (translatedBody as Record<string, unknown>).model === "string"
2748
+ ? (translatedBody as Record<string, unknown>).model
2749
+ : effectiveModel,
2750
+ ...(!Array.isArray((translatedBody as Record<string, unknown>).messages) &&
2751
+ Array.isArray((body as Record<string, unknown>).messages)
2752
+ ? { messages: (body as Record<string, unknown>).messages }
2753
+ : {}),
2754
+ }
2755
+ : translatedBody;
2756
+
2757
+ updatePendingScope(pendingScope, {
2758
+ providerRequest: registeredProviderRequest,
2759
+ });
2760
+ // T5: track which models we've tried for intra-family fallback
2761
+ const triedModels = new Set<string>([effectiveModel]);
2762
+ let currentModel = effectiveModel;
2763
+
2764
+ // Log start
2765
+ appendRequestLog({ model, provider, connectionId, status: "PENDING" }).catch(() => {});
2766
+
2767
+ const msgCount =
2768
+ translatedBody.messages?.length ||
2769
+ translatedBody.contents?.length ||
2770
+ translatedBody.request?.contents?.length ||
2771
+ (translatedBody.conversationState?.history?.length ?? 0) +
2772
+ (translatedBody.conversationState?.currentMessage ? 1 : 0) ||
2773
+ 0;
2774
+ log?.debug?.("REQUEST", `${provider?.toUpperCase()} | ${model} | ${msgCount} msgs`);
2775
+
2776
+ // ── Tier 2: Authoritative per-model/provider token-limit check (provider now resolved) ──
2777
+ if (apiKeyInfo?.id) {
2778
+ try {
2779
+ const tokenBreach = checkTokenLimits(
2780
+ apiKeyInfo.id,
2781
+ provider || undefined,
2782
+ model || undefined
2783
+ );
2784
+ if (tokenBreach) {
2785
+ const scopeLabel =
2786
+ tokenBreach.scopeType === "global"
2787
+ ? "account"
2788
+ : `${tokenBreach.scopeType} "${tokenBreach.scopeValue}"`;
2789
+ // FIX 6: clear the pending request marker before the early return so we do
2790
+ // not leak a phantom pending request (start was tracked at line ~1847).
2791
+ trackPendingRequest(model, provider, connectionId, false);
2792
+ // FIX 5: tag this as a per-API-key token-limit breach (errorCode
2793
+ // TOKEN_LIMIT_EXCEEDED) so the combo loop can distinguish it from an
2794
+ // upstream 429 and NOT cool shared accounts / retry it transiently.
2795
+ return createErrorResult(
2796
+ HTTP_STATUS.RATE_LIMITED,
2797
+ `Token limit exceeded for ${scopeLabel}: ${tokenBreach.tokensUsed}/${tokenBreach.limitValue} tokens used in the current window. Please try again later.`,
2798
+ null,
2799
+ "TOKEN_LIMIT_EXCEEDED"
2800
+ );
2801
+ }
2802
+ } catch (err) {
2803
+ // Fail-open at Tier 2: Tier 1 already enforced the model/global limit pre-dispatch.
2804
+ // A transient counter read error here must not break an otherwise-valid request.
2805
+ log?.warn?.("TOKEN_LIMIT", "Tier 2 token-limit check failed; allowing request", { err });
2806
+ }
2807
+ }
2808
+
2809
+ // Execute request using executor (handles URL building, headers, fallback, transform)
2810
+ let providerResponse;
2811
+ let providerUrl;
2812
+ let providerHeaders;
2813
+ let finalBody;
2814
+ let claudePromptCacheLogMeta = null;
2815
+
2816
+ try {
2817
+ const result = await executeProviderRequest(effectiveModel, true);
2818
+
2819
+ providerResponse = result.response;
2820
+ providerUrl = result.url;
2821
+ providerHeaders = result.headers;
2822
+ finalBody = providerRequestCapture.body(result.transformedBody);
2823
+ const responseConnectionId = getCurrentConnectionId();
2824
+ effectiveServiceTier = resolveEffectiveServiceTier(finalBody);
2825
+ claudePromptCacheLogMeta = buildClaudePromptCacheLogMeta(
2826
+ targetFormat,
2827
+ finalBody,
2828
+ providerHeaders,
2829
+ clientRawRequest?.headers
2830
+ );
2831
+
2832
+ // Log target request (final request to provider)
2833
+ reqLogger.logTargetRequest(providerUrl, providerHeaders, finalBody);
2834
+ updatePendingScope(pendingScope, {
2835
+ providerRequest: finalBody,
2836
+ providerUrl,
2837
+ stage: "provider_response_started",
2838
+ });
2839
+ // Update rate limiter from response headers (learn limits dynamically)
2840
+ updateFromHeaders(
2841
+ provider,
2842
+ responseConnectionId,
2843
+ providerResponse.headers,
2844
+ providerResponse.status,
2845
+ model
2846
+ );
2847
+
2848
+ // Store rate-limit headers for quota saturation signals
2849
+ try {
2850
+ const { storeRateLimitHeaders } = await import("@/lib/quota/saturationSignals");
2851
+ storeRateLimitHeaders(
2852
+ responseConnectionId,
2853
+ provider,
2854
+ providerResponse.headers as Record<string, string>
2855
+ );
2856
+ } catch {
2857
+ // fail-open: saturation signal is best-effort
2858
+ }
2859
+ } catch (error) {
2860
+ trackPendingRequest(model, provider, connectionId, false);
2861
+ if (isSemaphoreCapacityError(error)) {
2862
+ appendRequestLog({
2863
+ model,
2864
+ provider,
2865
+ connectionId,
2866
+ status: `FAILED ${error.code}`,
2867
+ }).catch(() => {});
2868
+ const failureMessage = error.message || "Semaphore timeout";
2869
+ persistAttemptLogs({
2870
+ status: HTTP_STATUS.RATE_LIMITED,
2871
+ error: failureMessage,
2872
+ providerRequest: finalBody || translatedBody,
2873
+ clientResponse: buildErrorBody(HTTP_STATUS.RATE_LIMITED, failureMessage),
2874
+ claudeCacheMeta: claudePromptCacheLogMeta,
2875
+ cacheSource: "upstream",
2876
+ });
2877
+ persistFailureUsage(HTTP_STATUS.RATE_LIMITED, error.code);
2878
+ const result = stream
2879
+ ? createStreamingErrorResult(HTTP_STATUS.RATE_LIMITED, failureMessage, error.code)
2880
+ : createErrorResult(HTTP_STATUS.RATE_LIMITED, failureMessage);
2881
+ return {
2882
+ ...result,
2883
+ errorType: "account_semaphore_capacity",
2884
+ errorCode: error.code,
2885
+ };
2886
+ }
2887
+ const failureStatus =
2888
+ error.name === "AbortError"
2889
+ ? 499
2890
+ : error.name === "TimeoutError" || error.name === "BodyTimeoutError"
2891
+ ? HTTP_STATUS.GATEWAY_TIMEOUT
2892
+ : error.status && typeof error.status === "number"
2893
+ ? error.status
2894
+ : HTTP_STATUS.BAD_GATEWAY;
2895
+ const failureMessage =
2896
+ error.name === "AbortError"
2897
+ ? "Request aborted"
2898
+ : formatProviderError(error, provider, model, failureStatus);
2899
+ const upstreamErrorCode = getUpstreamErrorIdentifier(error);
2900
+ // Tag our own deadline timeouts (fetch-start TimeoutError / body BodyTimeoutError,
2901
+ // both surfaced as a 504) as "upstream_timeout" so the cooldown layer can tell a
2902
+ // slow-but-not-failed request apart from a real provider 5xx. (Antigravity already
2903
+ // tags its pre-response timeout via the code below.)
2904
+ const isOwnDeadlineTimeout =
2905
+ failureStatus === HTTP_STATUS.GATEWAY_TIMEOUT &&
2906
+ (error.name === "TimeoutError" || error.name === "BodyTimeoutError");
2907
+ const upstreamErrorType =
2908
+ upstreamErrorCode === ANTIGRAVITY_PRE_RESPONSE_TIMEOUT_CODE || isOwnDeadlineTimeout
2909
+ ? "upstream_timeout"
2910
+ : failureStatus === 401
2911
+ ? "authentication_error"
2912
+ : undefined;
2913
+ appendRequestLog({
2914
+ model,
2915
+ provider,
2916
+ connectionId,
2917
+ status: `FAILED ${failureStatus}`,
2918
+ }).catch(() => {});
2919
+ persistAttemptLogs({
2920
+ status: failureStatus,
2921
+ error: failureMessage,
2922
+ providerRequest: finalBody || translatedBody,
2923
+ clientResponse: buildErrorBody(failureStatus, failureMessage),
2924
+ claudeCacheMeta: claudePromptCacheLogMeta,
2925
+ cacheSource: "upstream",
2926
+ });
2927
+ if (error.name === "AbortError") {
2928
+ streamController.handleError(error);
2929
+ return createErrorResult(499, "Request aborted");
2930
+ }
2931
+ persistFailureUsage(
2932
+ failureStatus,
2933
+ upstreamErrorCode || (error instanceof Error && error.name ? error.name : "upstream_error")
2934
+ );
2935
+ console.log(`${COLORS.red}[ERROR] ${failureMessage}${COLORS.reset}`);
2936
+ if (stream && upstreamErrorCode) {
2937
+ const result = createStreamingErrorResult(
2938
+ failureStatus,
2939
+ failureMessage,
2940
+ upstreamErrorCode,
2941
+ upstreamErrorType
2942
+ );
2943
+ return {
2944
+ ...result,
2945
+ errorType: upstreamErrorType,
2946
+ errorCode: upstreamErrorCode,
2947
+ };
2948
+ }
2949
+ return createErrorResult(
2950
+ failureStatus,
2951
+ failureMessage,
2952
+ null,
2953
+ upstreamErrorCode,
2954
+ upstreamErrorType
2955
+ );
2956
+ }
2957
+ // We need to peek at the error text if it's 400 for Qwen
2958
+ let upstreamErrorParsed = false;
2959
+ let parsedStatusCode = providerResponse.status;
2960
+ let parsedMessage = "";
2961
+ let parsedRetryAfterMs: number | null = null;
2962
+ let upstreamErrorBody: unknown = null;
2963
+
2964
+ if (provider === "qwen" && providerResponse.status === HTTP_STATUS.BAD_REQUEST) {
2965
+ const errorDetails = await parseUpstreamError(providerResponse, provider);
2966
+ parsedStatusCode = errorDetails.statusCode;
2967
+ parsedMessage = errorDetails.message;
2968
+ parsedRetryAfterMs = errorDetails.retryAfterMs;
2969
+ upstreamErrorBody = errorDetails.responseBody;
2970
+ upstreamErrorParsed = true;
2971
+ }
2972
+
2973
+ const errorMessageForToolDetection =
2974
+ typeof upstreamErrorBody === "string"
2975
+ ? upstreamErrorBody
2976
+ : JSON.stringify(upstreamErrorBody ?? {});
2977
+ if (shouldDetectLimit(errorMessageForToolDetection, parsedStatusCode)) {
2978
+ const detectedLimit = parseToolLimitFromError(errorMessageForToolDetection);
2979
+ if (detectedLimit) {
2980
+ setDetectedToolLimit(provider, detectedLimit);
2981
+ log?.info?.("TOOL_LIMIT", `Detected tool limit ${detectedLimit} for ${provider}`);
2982
+ }
2983
+ }
2984
+
2985
+ const isQwenExpiredError =
2986
+ provider === "qwen" &&
2987
+ parsedStatusCode === HTTP_STATUS.BAD_REQUEST &&
2988
+ parsedMessage?.toLowerCase().includes("session has expired");
2989
+
2990
+ // Track whether stream_options was present and stripped — if so, 401/403 after
2991
+ // that may be from the modification rather than a genuine auth failure, so we
2992
+ // skip the credential refresh attempt in that case.
2993
+ const hadStreamOptions =
2994
+ targetFormat === FORMATS.OPENAI_RESPONSES && "stream_options" in translatedBody;
2995
+ if (hadStreamOptions) {
2996
+ delete translatedBody.stream_options;
2997
+ }
2998
+
2999
+ // Handle 401/403 (and Qwen explicit expiration) - try token refresh using executor
3000
+ if (
3001
+ (providerResponse.status === HTTP_STATUS.UNAUTHORIZED ||
3002
+ providerResponse.status === HTTP_STATUS.FORBIDDEN ||
3003
+ isQwenExpiredError) &&
3004
+ !hadStreamOptions // Skip refresh if failure may be from stream_options removal, not auth
3005
+ ) {
3006
+ // Fix A: wrap refreshCredentials in runWithOnPersist so the persist callback
3007
+ // executes INSIDE the per-connection mutex held by getAccessToken. This makes
3008
+ // [network refresh + DB write + outer-state mutation] one atomic step and
3009
+ // prevents concurrent requests from reading a stale refreshToken before the
3010
+ // DB has been updated (refresh_token_reused on Codex/OpenAI).
3011
+ //
3012
+ // Not every executor routes refresh through getAccessToken (e.g. github.ts
3013
+ // calls refreshCopilotToken directly). When the persistFn doesn't fire from
3014
+ // inside getAccessToken, we still need to do the credentials mutation + user
3015
+ // callback after refreshCredentials returns. The `persistFnRan` flag tracks
3016
+ // which path executed so we don't double-fire (race-prone) or skip (regression).
3017
+ // Front 3: remember the refresh_token we are about to present so that, if the
3018
+ // refresh fails as unrecoverable, we can tell a genuine death apart from a
3019
+ // stale-token reuse that a concurrent/sibling refresh already rotated past.
3020
+ const attemptedRefreshToken =
3021
+ typeof credentials?.refreshToken === "string" ? credentials.refreshToken : null;
3022
+ let persistFnRan = false;
3023
+ const persistFn = onCredentialsRefreshed
3024
+ ? async (refreshResult: Record<string, unknown>) => {
3025
+ persistFnRan = true;
3026
+ // Mutate the shared credentials object so subsequent executor calls
3027
+ // in this request see the new tokens. Runs INSIDE the mutex.
3028
+ Object.assign(credentials, refreshResult);
3029
+ await onCredentialsRefreshed(refreshResult);
3030
+ }
3031
+ : undefined;
3032
+
3033
+ // #4038: build a compare-and-swap reread so getAccessToken can skip the persist if a
3034
+ // concurrent writer (sibling request / HealthCheck / replica) already rotated this
3035
+ // connection's refresh_token past the one we presented — overwriting would revert it
3036
+ // and revoke the token family. No connectionId ⇒ no guard (behavior unchanged).
3037
+ const casConnectionId =
3038
+ typeof credentials?.connectionId === "string" ? credentials.connectionId.trim() : "";
3039
+ const casReread = casConnectionId
3040
+ ? async () => {
3041
+ const latest = await getProviderConnectionById(casConnectionId);
3042
+ return typeof latest?.refreshToken === "string" ? latest.refreshToken : null;
3043
+ }
3044
+ : null;
3045
+
3046
+ const newCredentials = (await refreshWithRetry(
3047
+ () =>
3048
+ runWithCasGuard(
3049
+ casReread ? { expectedRefreshToken: attemptedRefreshToken, reread: casReread } : null,
3050
+ () => runWithOnPersist(persistFn, () => executor.refreshCredentials(credentials, log))
3051
+ ),
3052
+ 3,
3053
+ log,
3054
+ provider // Explicitly pass the provider to avoid universally tripping the "unknown" circuit breaker
3055
+ )) as null | {
3056
+ accessToken?: string;
3057
+ copilotToken?: string;
3058
+ };
3059
+
3060
+ if (newCredentials?.accessToken || newCredentials?.copilotToken) {
3061
+ log?.info?.("TOKEN", `${provider?.toUpperCase()} | refreshed`);
3062
+
3063
+ // Fall back to post-mutex mutation only for executors that don't route
3064
+ // through getAccessToken (and therefore never fire onPersist). For
3065
+ // executors that DO route through it (Codex, Claude, Gemini, etc.) the
3066
+ // mutation already happened atomically inside the mutex.
3067
+ if (!persistFnRan) {
3068
+ Object.assign(credentials, newCredentials);
3069
+ if (onCredentialsRefreshed) {
3070
+ await onCredentialsRefreshed(newCredentials);
3071
+ }
3072
+ }
3073
+
3074
+ // Retry with new credentials — model + extra headers follow translatedBody.model so they
3075
+ // stay aligned if this block ever runs after a path that mutates body.model (e.g. fallback).
3076
+ try {
3077
+ const retryModelId = String(translatedBody.model || effectiveModel);
3078
+ const retryResult = await runWithCapture(providerRequestCapture, () =>
3079
+ executor.execute({
3080
+ model: retryModelId,
3081
+ body: translatedBody,
3082
+ stream: upstreamStream,
3083
+ credentials: getExecutionCredentials(),
3084
+ signal: streamController.signal,
3085
+ log,
3086
+ extendedContext,
3087
+ upstreamExtraHeaders: buildUpstreamHeadersForExecute(retryModelId),
3088
+ clientHeaders: buildExecutorClientHeaders(clientRawRequest?.headers, userAgent),
3089
+ onCredentialsRefreshed,
3090
+ skipUpstreamRetry: isCombo,
3091
+ contextEditing: { enabled: contextEditingEnabled },
3092
+ })
3093
+ );
3094
+
3095
+ if (retryResult.response.ok) {
3096
+ providerResponse = retryResult.response;
3097
+ providerUrl = retryResult.url;
3098
+ providerHeaders = new Headers(retryResult.headers || {});
3099
+ finalBody = providerRequestCapture.body(retryResult.transformedBody);
3100
+ reqLogger.logTargetRequest(providerUrl, providerHeaders, finalBody);
3101
+ updatePendingScope(pendingScope, {
3102
+ providerRequest: finalBody,
3103
+ providerUrl,
3104
+ stage: "provider_response_started",
3105
+ });
3106
+ upstreamErrorParsed = false; // Reset since new response is OK
3107
+ } else {
3108
+ providerResponse = retryResult.response;
3109
+ upstreamErrorParsed = false; // Let it be parsed downstream
3110
+ }
3111
+ } catch (retryErr) {
3112
+ // Refresh succeeded but the retry leg failed (network blip, AbortError,
3113
+ // executor throw). Don't swallow — the operator-visible signal "the user
3114
+ // saw 401 even though auth was actually fixed" is much more confusing
3115
+ // than the original 401 alone. Surface at error level with sanitization.
3116
+ log?.error?.(
3117
+ "TOKEN",
3118
+ `${provider?.toUpperCase()} | retry after refresh failed: ${sanitizeErrorMessage(retryErr)}`
3119
+ );
3120
+ }
3121
+ } else {
3122
+ log?.warn?.("TOKEN", `${provider?.toUpperCase()} | refresh failed`);
3123
+ if (isUnrecoverableRefreshError(newCredentials) && onCredentialsRefreshed) {
3124
+ // Front 3 (reuse-race tolerance): before deactivating, re-read the DB.
3125
+ // If a sibling/concurrent refresh already rotated this connection's
3126
+ // refresh_token (common for Codex/OpenAI under one shared Auth0 client),
3127
+ // the failure we saw was a stale-token reuse — the account is healthy
3128
+ // with the newer token, so keep it active instead of killing it.
3129
+ let alreadyRotated = false;
3130
+ if (typeof connectionId === "string" && connectionId && attemptedRefreshToken) {
3131
+ try {
3132
+ const latest = await getProviderConnectionById(connectionId);
3133
+ if (wasRefreshTokenRotated(attemptedRefreshToken, latest?.refreshToken)) {
3134
+ alreadyRotated = true;
3135
+ log?.warn?.(
3136
+ "TOKEN",
3137
+ `${provider.toUpperCase()} | refresh_token already rotated by a concurrent refresh — keeping connection active`
3138
+ );
3139
+ }
3140
+ } catch {
3141
+ // DB read failed — fall through to the safe default (deactivate).
3142
+ }
3143
+ }
3144
+ if (!alreadyRotated) {
3145
+ await onCredentialsRefreshed({ testStatus: "expired", isActive: false });
3146
+ }
3147
+ }
3148
+ }
3149
+ }
3150
+
3151
+ await persistCodexQuotaState(normalizeHeaders(providerResponse.headers), providerResponse.status);
3152
+
3153
+ // Check provider response - return error info for fallback handling
3154
+ if (!providerResponse.ok) {
3155
+ trackPendingRequest(model, provider, connectionId, false);
3156
+
3157
+ let statusCode = providerResponse.status;
3158
+ let message = "";
3159
+ let retryAfterMs: number | null = null;
3160
+ let upstreamErrorCode: string | undefined;
3161
+ let upstreamErrorType: string | undefined;
3162
+
3163
+ if (upstreamErrorParsed) {
3164
+ statusCode = parsedStatusCode;
3165
+ message = parsedMessage;
3166
+ retryAfterMs = parsedRetryAfterMs;
3167
+ } else {
3168
+ const details = await parseUpstreamError(providerResponse, provider);
3169
+ statusCode = details.statusCode;
3170
+ message = details.message;
3171
+ retryAfterMs = details.retryAfterMs;
3172
+ upstreamErrorBody = details.responseBody;
3173
+ upstreamErrorCode = details.errorCode as string | undefined;
3174
+ upstreamErrorType = details.errorType as string | undefined;
3175
+ }
3176
+
3177
+ // T06/T10/T36: classify provider errors and persist terminal account states.
3178
+ let errorType = classifyProviderError(statusCode, message, provider);
3179
+ if (statusCode === 429 && isModelScope()) {
3180
+ const decision = classifyModelScope429(message, normalizeHeaders(providerResponse.headers));
3181
+ errorType =
3182
+ decision.kind === "quota_exhausted"
3183
+ ? PROVIDER_ERROR_TYPES.QUOTA_EXHAUSTED
3184
+ : PROVIDER_ERROR_TYPES.RATE_LIMITED;
3185
+ log?.warn?.(
3186
+ "MODELSCOPE_429",
3187
+ `${decision.kind} (model remaining: ${decision.snapshot.modelRemaining ?? "unknown"}, total remaining: ${decision.snapshot.totalRemaining ?? "unknown"})`
3188
+ );
3189
+ }
3190
+ const errorConnectionId = getCurrentConnectionId();
3191
+ if (errorConnectionId && errorType) {
3192
+ try {
3193
+ if (errorType === PROVIDER_ERROR_TYPES.FORBIDDEN) {
3194
+ await updateProviderConnection(errorConnectionId, {
3195
+ isActive: false,
3196
+ testStatus: "banned",
3197
+ lastErrorType: errorType,
3198
+ lastError: message,
3199
+ errorCode: statusCode,
3200
+ });
3201
+ console.warn(
3202
+ `[provider] Node ${errorConnectionId} banned (${statusCode}) — disabling permanently`
3203
+ );
3204
+ } else if (errorType === PROVIDER_ERROR_TYPES.ACCOUNT_DEACTIVATED) {
3205
+ // Plan A: if connection has extra API keys, don't disable — only the failing key is affected.
3206
+ // Single-key connections still get disabled as before.
3207
+ if (
3208
+ connectionHasExtraKeys(
3209
+ errorConnectionId,
3210
+ (credentials?.providerSpecificData as Record<string, unknown> | undefined)
3211
+ ?.extraApiKeys as string[] | undefined
3212
+ )
3213
+ ) {
3214
+ await updateProviderConnection(errorConnectionId, {
3215
+ lastErrorType: errorType,
3216
+ lastError: message,
3217
+ errorCode: statusCode,
3218
+ });
3219
+ console.warn(
3220
+ `[provider] Node ${errorConnectionId} account deactivated (${statusCode}) — has extra keys, keeping connection active`
3221
+ );
3222
+ } else {
3223
+ await updateProviderConnection(errorConnectionId, {
3224
+ isActive: false,
3225
+ testStatus: "deactivated",
3226
+ lastErrorType: errorType,
3227
+ lastError: message,
3228
+ errorCode: statusCode,
3229
+ });
3230
+ console.warn(
3231
+ `[provider] Node ${errorConnectionId} account deactivated (${statusCode}) — disabling permanently`
3232
+ );
3233
+ }
3234
+ } else if (errorType === PROVIDER_ERROR_TYPES.QUOTA_EXHAUSTED) {
3235
+ // Providers with per-model quotas — lock the model only, not the connection
3236
+ const quotaCooldownMs = retryAfterMs || COOLDOWN_MS.rateLimit;
3237
+ const accountSemaphoreKey = resolveAccountSemaphoreKey({
3238
+ provider,
3239
+ model: currentModel,
3240
+ connectionId: errorConnectionId,
3241
+ credentials,
3242
+ });
3243
+ if (accountSemaphoreKey) {
3244
+ markAccountSemaphoreBlocked(accountSemaphoreKey, quotaCooldownMs);
3245
+ }
3246
+ if (isModelScope() && errorConnectionId) {
3247
+ lockModel(provider, errorConnectionId, model, "quota_exhausted", quotaCooldownMs);
3248
+ console.warn(
3249
+ `[provider] Node ${errorConnectionId} ModelScope model quota exhausted (${statusCode}) for ${model} - ${Math.ceil(quotaCooldownMs / 1000)}s (connection stays active)`
3250
+ );
3251
+ } else if (
3252
+ lockModelIfPerModelQuota(
3253
+ provider,
3254
+ errorConnectionId,
3255
+ model,
3256
+ "quota_exhausted",
3257
+ quotaCooldownMs
3258
+ )
3259
+ ) {
3260
+ const quotaScope = getQuotaScopeLabelForProvider(provider, model);
3261
+ console.warn(
3262
+ `[provider] Node ${errorConnectionId} ${quotaScope}-only quota exhausted (${statusCode}) for ${model} - ${Math.ceil(quotaCooldownMs / 1000)}s (cooldown_scope=${quotaScope}, ttl_source=${retryAfterMs ? "upstream" : "inferred"}, connection stays active)`
3263
+ );
3264
+ } else {
3265
+ await updateProviderConnection(errorConnectionId, {
3266
+ testStatus: "credits_exhausted",
3267
+ lastErrorType: errorType,
3268
+ lastError: message,
3269
+ errorCode: statusCode,
3270
+ });
3271
+ console.warn(`[provider] Node ${errorConnectionId} exhausted quota (${statusCode})`);
3272
+ }
3273
+ } else if (errorType === PROVIDER_ERROR_TYPES.UNAUTHORIZED) {
3274
+ // Normal 401 (token/session auth issue): keep account active for refresh/re-auth.
3275
+ await updateProviderConnection(errorConnectionId, {
3276
+ lastErrorType: errorType,
3277
+ lastError: message,
3278
+ errorCode: statusCode,
3279
+ });
3280
+ } else if (errorType === PROVIDER_ERROR_TYPES.OAUTH_INVALID_TOKEN) {
3281
+ // OAuth 401 with invalid credentials - token refresh can recover
3282
+ await updateProviderConnection(errorConnectionId, {
3283
+ lastErrorType: errorType,
3284
+ lastError: message,
3285
+ errorCode: statusCode,
3286
+ });
3287
+ console.warn(
3288
+ `[provider] Node ${errorConnectionId} OAuth token invalid (${statusCode}) — token refresh available`
3289
+ );
3290
+ } else if (errorType === PROVIDER_ERROR_TYPES.PROJECT_ROUTE_ERROR) {
3291
+ // Cloud Code 403 with stale project: not a ban, keep account active.
3292
+ await updateProviderConnection(errorConnectionId, {
3293
+ lastErrorType: errorType,
3294
+ lastError: message,
3295
+ errorCode: statusCode,
3296
+ });
3297
+ console.warn(
3298
+ `[provider] Node ${errorConnectionId} project routing error (${statusCode}) — not banning`
3299
+ );
3300
+ }
3301
+ } catch {
3302
+ // Best-effort state update; request flow should continue with fallback handling.
3303
+ }
3304
+ }
3305
+
3306
+ appendRequestLog({
3307
+ model,
3308
+ provider,
3309
+ connectionId: errorConnectionId,
3310
+ status: `FAILED ${statusCode}`,
3311
+ }).catch(() => {});
3312
+
3313
+ const errMsg = formatProviderError(new Error(message), provider, model, statusCode);
3314
+ console.log(`${COLORS.red}[ERROR] ${errMsg}${COLORS.reset}`);
3315
+
3316
+ // Log Antigravity retry time if available
3317
+ if (retryAfterMs && provider === "antigravity") {
3318
+ const retrySeconds = Math.ceil(retryAfterMs / 1000);
3319
+ log?.debug?.("RETRY", `Antigravity quota reset in ${retrySeconds}s (${retryAfterMs}ms)`);
3320
+ }
3321
+
3322
+ // Log error with full request body for debugging
3323
+ reqLogger.logError(new Error(message), finalBody || translatedBody);
3324
+ reqLogger.logProviderResponse(
3325
+ providerResponse.status,
3326
+ providerResponse.statusText,
3327
+ providerResponse.headers,
3328
+ upstreamErrorBody
3329
+ );
3330
+
3331
+ // Update rate limiter from error response headers
3332
+ updateFromHeaders(provider, errorConnectionId, providerResponse.headers, statusCode, model);
3333
+ if (errorConnectionId && upstreamErrorBody !== null && upstreamErrorBody !== undefined) {
3334
+ updateFromResponseBody(provider, errorConnectionId, upstreamErrorBody, statusCode, model);
3335
+ }
3336
+
3337
+ // ── T5: Intra-family model fallback ──────────────────────────────────────
3338
+ // Before returning a model-unavailable error upstream, try sibling models
3339
+ // from the same family. This keeps the request alive on the same account
3340
+ // instead of failing the entire combo.
3341
+ if (isModelUnavailableError(statusCode, message)) {
3342
+ const nextModel = getNextFamilyFallback(currentModel, triedModels);
3343
+ if (nextModel) {
3344
+ triedModels.add(nextModel);
3345
+ currentModel = nextModel;
3346
+ translatedBody.model = nextModel;
3347
+ log?.info?.("MODEL_FALLBACK", `${model} unavailable (${statusCode}) → trying ${nextModel}`);
3348
+ // Re-execute with the fallback model
3349
+ try {
3350
+ const fallbackResult = await executeProviderRequest(nextModel, false);
3351
+ if (fallbackResult.response.ok) {
3352
+ providerResponse = fallbackResult.response;
3353
+ providerUrl = fallbackResult.url;
3354
+ providerHeaders = fallbackResult.headers;
3355
+ finalBody = providerRequestCapture.body(fallbackResult.transformedBody);
3356
+ reqLogger.logTargetRequest(providerUrl, providerHeaders, finalBody);
3357
+ updatePendingScope(pendingScope, {
3358
+ providerRequest: finalBody,
3359
+ providerUrl,
3360
+ stage: "provider_response_started",
3361
+ });
3362
+ // Continue processing with the fallback response — skip error return
3363
+ log?.info?.("MODEL_FALLBACK", `Serving ${nextModel} as fallback for ${model}`);
3364
+ // Jump to streaming/non-streaming handling below
3365
+ // We fall through by NOT returning here
3366
+ } else {
3367
+ // Fallback also failed — return original error
3368
+ persistAttemptLogs({
3369
+ status: statusCode,
3370
+ error: errMsg,
3371
+ providerRequest: finalBody || translatedBody,
3372
+ providerResponse: upstreamErrorBody,
3373
+ clientResponse: buildErrorBody(statusCode, errMsg),
3374
+ cacheSource: "upstream",
3375
+ });
3376
+ persistFailureUsage(statusCode, "model_unavailable");
3377
+ return createErrorResult(
3378
+ statusCode,
3379
+ errMsg,
3380
+ retryAfterMs,
3381
+ upstreamErrorCode,
3382
+ upstreamErrorType,
3383
+ upstreamErrorBody
3384
+ );
3385
+ }
3386
+ } catch {
3387
+ persistAttemptLogs({
3388
+ status: statusCode,
3389
+ error: errMsg,
3390
+ providerRequest: finalBody || translatedBody,
3391
+ providerResponse: upstreamErrorBody,
3392
+ clientResponse: buildErrorBody(statusCode, errMsg),
3393
+ cacheSource: "upstream",
3394
+ });
3395
+ persistFailureUsage(statusCode, "model_unavailable");
3396
+ return createErrorResult(
3397
+ statusCode,
3398
+ errMsg,
3399
+ retryAfterMs,
3400
+ upstreamErrorCode,
3401
+ upstreamErrorType,
3402
+ upstreamErrorBody
3403
+ );
3404
+ }
3405
+ } else {
3406
+ persistAttemptLogs({
3407
+ status: statusCode,
3408
+ error: errMsg,
3409
+ providerRequest: finalBody || translatedBody,
3410
+ providerResponse: upstreamErrorBody,
3411
+ clientResponse: buildErrorBody(statusCode, errMsg),
3412
+ cacheSource: "upstream",
3413
+ });
3414
+ persistFailureUsage(statusCode, "model_unavailable");
3415
+ return createErrorResult(
3416
+ statusCode,
3417
+ errMsg,
3418
+ retryAfterMs,
3419
+ upstreamErrorCode,
3420
+ upstreamErrorType,
3421
+ upstreamErrorBody
3422
+ );
3423
+ }
3424
+ } else if (isContextOverflowError(statusCode, message)) {
3425
+ const familyCandidates = getModelFamily(currentModel).filter(
3426
+ (m) => m !== currentModel && !triedModels.has(m)
3427
+ );
3428
+ const nextModel =
3429
+ findLargerContextModel(currentModel, familyCandidates) ??
3430
+ getNextFamilyFallback(currentModel, triedModels);
3431
+ if (nextModel) {
3432
+ triedModels.add(nextModel);
3433
+ currentModel = nextModel;
3434
+ translatedBody.model = nextModel;
3435
+ log?.info?.("CONTEXT_OVERFLOW_FALLBACK", `${model} context overflow → trying ${nextModel}`);
3436
+ try {
3437
+ const fallbackResult = await executeProviderRequest(nextModel, false);
3438
+ if (fallbackResult.response.ok) {
3439
+ providerResponse = fallbackResult.response;
3440
+ providerUrl = fallbackResult.url;
3441
+ providerHeaders = fallbackResult.headers;
3442
+ finalBody = providerRequestCapture.body(fallbackResult.transformedBody);
3443
+ reqLogger.logTargetRequest(providerUrl, providerHeaders, finalBody);
3444
+ updatePendingScope(pendingScope, {
3445
+ providerRequest: finalBody,
3446
+ providerUrl,
3447
+ stage: "provider_response_started",
3448
+ });
3449
+ log?.info?.(
3450
+ "CONTEXT_OVERFLOW_FALLBACK",
3451
+ `Serving ${nextModel} as fallback for ${model}`
3452
+ );
3453
+ } else {
3454
+ persistAttemptLogs({
3455
+ status: statusCode,
3456
+ error: errMsg,
3457
+ providerRequest: finalBody || translatedBody,
3458
+ providerResponse: upstreamErrorBody,
3459
+ clientResponse: buildErrorBody(statusCode, errMsg),
3460
+ cacheSource: "upstream",
3461
+ });
3462
+ persistFailureUsage(statusCode, "context_overflow");
3463
+ return createErrorResult(
3464
+ statusCode,
3465
+ errMsg,
3466
+ retryAfterMs,
3467
+ upstreamErrorCode,
3468
+ upstreamErrorType,
3469
+ upstreamErrorBody
3470
+ );
3471
+ }
3472
+ } catch {
3473
+ persistAttemptLogs({
3474
+ status: statusCode,
3475
+ error: errMsg,
3476
+ providerRequest: finalBody || translatedBody,
3477
+ providerResponse: upstreamErrorBody,
3478
+ clientResponse: buildErrorBody(statusCode, errMsg),
3479
+ cacheSource: "upstream",
3480
+ });
3481
+ persistFailureUsage(statusCode, "context_overflow");
3482
+ return createErrorResult(
3483
+ statusCode,
3484
+ errMsg,
3485
+ retryAfterMs,
3486
+ upstreamErrorCode,
3487
+ upstreamErrorType,
3488
+ upstreamErrorBody
3489
+ );
3490
+ }
3491
+ } else {
3492
+ persistAttemptLogs({
3493
+ status: statusCode,
3494
+ error: errMsg,
3495
+ providerRequest: finalBody || translatedBody,
3496
+ providerResponse: upstreamErrorBody,
3497
+ clientResponse: buildErrorBody(statusCode, errMsg),
3498
+ cacheSource: "upstream",
3499
+ });
3500
+ persistFailureUsage(statusCode, "context_overflow");
3501
+ return createErrorResult(
3502
+ statusCode,
3503
+ errMsg,
3504
+ retryAfterMs,
3505
+ upstreamErrorCode,
3506
+ upstreamErrorType,
3507
+ upstreamErrorBody
3508
+ );
3509
+ }
3510
+ } else {
3511
+ persistAttemptLogs({
3512
+ status: statusCode,
3513
+ error: errMsg,
3514
+ providerRequest: finalBody || translatedBody,
3515
+ providerResponse: upstreamErrorBody,
3516
+ clientResponse: buildErrorBody(statusCode, errMsg),
3517
+ cacheSource: "upstream",
3518
+ });
3519
+ persistFailureUsage(statusCode, `upstream_${statusCode}`);
3520
+
3521
+ // Emergency budget fallback is orchestrated exclusively by the routing layer
3522
+ // (src/sse/handlers/chat.ts), which resolves credentials FOR the emergency
3523
+ // provider through account selection. The executor-level hop that used to
3524
+ // live here re-sent the FAILING provider's credentials to the emergency
3525
+ // provider's endpoint (e.g. the OpenAI API key to integrate.api.nvidia.com)
3526
+ // — a cross-provider credential leak that also never succeeded upstream.
3527
+ return createErrorResult(
3528
+ statusCode,
3529
+ errMsg,
3530
+ retryAfterMs,
3531
+ upstreamErrorCode,
3532
+ upstreamErrorType,
3533
+ upstreamErrorBody
3534
+ );
3535
+ }
3536
+ // ── End T5 ───────────────────────────────────────────────────────────────
3537
+ }
3538
+
3539
+ // Non-streaming response
3540
+ if (!stream) {
3541
+ const parsed = await parseNonStreamingResponseBody({
3542
+ providerResponse,
3543
+ upstreamStream,
3544
+ providerHeaders,
3545
+ finalBody,
3546
+ targetFormat,
3547
+ model,
3548
+ log,
3549
+ });
3550
+ const normalizedProviderPayload = parsed.normalizedProviderPayload;
3551
+ const looksLikeSSE = parsed.looksLikeSSE;
3552
+
3553
+ if (parsed.kind === "invalid_sse") {
3554
+ appendRequestLog({
3555
+ model,
3556
+ provider,
3557
+ connectionId,
3558
+ status: `FAILED ${HTTP_STATUS.BAD_GATEWAY}`,
3559
+ }).catch(() => {});
3560
+ const invalidSseMessage = parsed.message;
3561
+ persistAttemptLogs({
3562
+ status: HTTP_STATUS.BAD_GATEWAY,
3563
+ error: invalidSseMessage,
3564
+ providerRequest: finalBody || translatedBody,
3565
+ providerResponse: normalizedProviderPayload,
3566
+ clientResponse: buildErrorBody(HTTP_STATUS.BAD_GATEWAY, invalidSseMessage),
3567
+ cacheSource: "upstream",
3568
+ });
3569
+ persistFailureUsage(HTTP_STATUS.BAD_GATEWAY, "invalid_sse_payload");
3570
+ trackPendingRequest(model, provider, pendingConnId, false);
3571
+ return createErrorResult(HTTP_STATUS.BAD_GATEWAY, invalidSseMessage);
3572
+ }
3573
+
3574
+ if (parsed.kind === "invalid_json") {
3575
+ appendRequestLog({
3576
+ model,
3577
+ provider,
3578
+ connectionId,
3579
+ status: `FAILED ${HTTP_STATUS.BAD_GATEWAY}`,
3580
+ }).catch(() => {});
3581
+ const detailedError = parsed.detailedError;
3582
+ const invalidJsonMessage = parsed.message;
3583
+ persistAttemptLogs({
3584
+ status: HTTP_STATUS.BAD_GATEWAY,
3585
+ error: detailedError,
3586
+ providerRequest: finalBody || translatedBody,
3587
+ providerResponse: normalizedProviderPayload,
3588
+ clientResponse: buildErrorBody(HTTP_STATUS.BAD_GATEWAY, invalidJsonMessage),
3589
+ cacheSource: "upstream",
3590
+ });
3591
+ persistFailureUsage(HTTP_STATUS.BAD_GATEWAY, "invalid_json_payload");
3592
+ trackPendingRequest(model, provider, connectionId, false);
3593
+ return createErrorResult(HTTP_STATUS.BAD_GATEWAY, invalidJsonMessage);
3594
+ }
3595
+
3596
+ let responseBody = parsed.responseBody;
3597
+ let responsePayloadFormat = parsed.responsePayloadFormat;
3598
+
3599
+ // ── ClinePass {success,data} envelope unwrap (before translation) ──────────
3600
+ // ClinePass wraps non-streaming JSON in a {success, data} envelope; errors
3601
+ // use {success:false, error}. Transient {success:false, error:"empty..."}
3602
+ // responses get one 2s retry before surfacing. CLINEPASS-GATED — untouched
3603
+ // for every other provider. Envelope errors route through createErrorResult
3604
+ // (→ buildErrorBody/sanitizeErrorMessage, Rule #12).
3605
+ if (provider === "clinepass") {
3606
+ let { body: unwrapped, error: envError } = unwrapClinepassEnvelope(responseBody, provider);
3607
+ if (envError && /empty/i.test(envError.message || "")) {
3608
+ log?.warn?.("RETRY", "clinepass returned empty content, retrying once after 2s");
3609
+ await new Promise((r) => setTimeout(r, 2000));
3610
+ try {
3611
+ const retryResult = await executeProviderRequest(effectiveModel, false);
3612
+ if (retryResult?.response?.ok) {
3613
+ const retryParsed = await parseNonStreamingResponseBody({
3614
+ providerResponse: retryResult.response,
3615
+ upstreamStream: undefined,
3616
+ providerHeaders: retryResult.headers,
3617
+ finalBody: retryResult.transformedBody,
3618
+ targetFormat,
3619
+ model,
3620
+ log,
3621
+ });
3622
+ if (retryParsed.kind !== "invalid_sse" && retryParsed.kind !== "invalid_json") {
3623
+ providerResponse = retryResult.response;
3624
+ providerUrl = retryResult.url;
3625
+ providerHeaders = retryResult.headers;
3626
+ finalBody = providerRequestCapture.body(retryResult.transformedBody);
3627
+ ({ body: unwrapped, error: envError } = unwrapClinepassEnvelope(
3628
+ retryParsed.responseBody,
3629
+ provider
3630
+ ));
3631
+ }
3632
+ }
3633
+ } catch (retryErr) {
3634
+ log?.warn?.(
3635
+ "RETRY",
3636
+ `clinepass retry failed: ${
3637
+ retryErr instanceof Error ? retryErr.message : String(retryErr)
3638
+ }`
3639
+ );
3640
+ }
3641
+ }
3642
+ if (envError) {
3643
+ appendRequestLog({
3644
+ model,
3645
+ provider,
3646
+ connectionId,
3647
+ status: `FAILED ${HTTP_STATUS.BAD_GATEWAY}`,
3648
+ }).catch(() => {});
3649
+ persistFailureUsage(HTTP_STATUS.BAD_GATEWAY, "clinepass_envelope_error");
3650
+ trackPendingRequest(model, provider, connectionId, false);
3651
+ return createErrorResult(HTTP_STATUS.BAD_GATEWAY, envError.message);
3652
+ }
3653
+ responseBody = unwrapped;
3654
+ }
3655
+ responseBody = unwrapClineNonStreamingEnvelope(provider, responseBody);
3656
+
3657
+ // Check for empty content response (fake success) - trigger fallback
3658
+ if (isEmptyContentResponse(responseBody)) {
3659
+ appendRequestLog({
3660
+ model,
3661
+ provider,
3662
+ connectionId,
3663
+ status: `FAILED ${HTTP_STATUS.BAD_GATEWAY}`,
3664
+ }).catch(() => {});
3665
+ const emptyContentMessage = "Provider returned empty content";
3666
+ persistAttemptLogs({
3667
+ status: HTTP_STATUS.BAD_GATEWAY,
3668
+ error: emptyContentMessage,
3669
+ providerRequest: finalBody || translatedBody,
3670
+ providerResponse: normalizedProviderPayload,
3671
+ clientResponse: buildErrorBody(HTTP_STATUS.BAD_GATEWAY, emptyContentMessage),
3672
+ cacheSource: "upstream",
3673
+ });
3674
+ persistFailureUsage(HTTP_STATUS.BAD_GATEWAY, "empty_content");
3675
+
3676
+ // Trigger non-recursive fallback for empty content
3677
+ const nextModel = getNextFamilyFallback(currentModel, triedModels);
3678
+ if (nextModel) {
3679
+ triedModels.add(nextModel);
3680
+ currentModel = nextModel;
3681
+ translatedBody.model = nextModel;
3682
+ log?.info?.(
3683
+ "EMPTY_CONTENT_FALLBACK",
3684
+ `${model} returned empty content → trying ${nextModel}`
3685
+ );
3686
+ try {
3687
+ const fallbackResult = await executeProviderRequest(nextModel, false);
3688
+ if (fallbackResult.response.ok) {
3689
+ const fallbackRaw = await withBodyTimeout<string>(fallbackResult.response.text());
3690
+ try {
3691
+ responseBody = fallbackRaw ? JSON.parse(fallbackRaw) : {};
3692
+ providerUrl = fallbackResult.url;
3693
+ providerHeaders = fallbackResult.headers;
3694
+ finalBody = providerRequestCapture.body(fallbackResult.transformedBody);
3695
+ reqLogger.logTargetRequest(providerUrl, providerHeaders, finalBody);
3696
+ log?.info?.(
3697
+ "EMPTY_CONTENT_FALLBACK",
3698
+ `Serving ${nextModel} as fallback for ${model}`
3699
+ );
3700
+ // Fall through — continue processing with the new responseBody
3701
+ } catch {
3702
+ trackPendingRequest(model, provider, connectionId, false);
3703
+ return createErrorResult(HTTP_STATUS.BAD_GATEWAY, emptyContentMessage);
3704
+ }
3705
+ } else {
3706
+ trackPendingRequest(model, provider, connectionId, false);
3707
+ return createErrorResult(HTTP_STATUS.BAD_GATEWAY, emptyContentMessage);
3708
+ }
3709
+ } catch {
3710
+ trackPendingRequest(model, provider, connectionId, false);
3711
+ return createErrorResult(HTTP_STATUS.BAD_GATEWAY, emptyContentMessage);
3712
+ }
3713
+ } else {
3714
+ trackPendingRequest(model, provider, connectionId, false);
3715
+ return createErrorResult(HTTP_STATUS.BAD_GATEWAY, emptyContentMessage);
3716
+ }
3717
+ }
3718
+
3719
+ const responseToolNameMap = mergeResponseToolNameMap(
3720
+ toolNameMap,
3721
+ (finalBody as Record<string, unknown> | null | undefined) ?? null
3722
+ );
3723
+
3724
+ if (sourceFormat === FORMATS.CLAUDE && targetFormat === FORMATS.CLAUDE) {
3725
+ responseBody = restoreClaudePassthroughToolNames(responseBody, responseToolNameMap);
3726
+ }
3727
+ reqLogger.logProviderResponse(
3728
+ providerResponse.status,
3729
+ providerResponse.statusText,
3730
+ providerResponse.headers,
3731
+ looksLikeSSE
3732
+ ? {
3733
+ _streamed: true,
3734
+ _format: "sse-json",
3735
+ summary: responseBody,
3736
+ }
3737
+ : responseBody
3738
+ );
3739
+ effectiveServiceTier = resolveReportedServiceTier(responseBody) ?? effectiveServiceTier;
3740
+
3741
+ // Notify success - caller can clear error status if needed
3742
+ if (onRequestSuccess) {
3743
+ await onRequestSuccess();
3744
+ }
3745
+ const successConnectionId = getCurrentConnectionId();
3746
+ await maybeSyncClaudeExtraUsageState({
3747
+ provider,
3748
+ connectionId: successConnectionId,
3749
+ providerSpecificData: credentials?.providerSpecificData,
3750
+ log,
3751
+ });
3752
+
3753
+ // Log usage for non-streaming responses
3754
+ const usage = extractUsageFromResponse(responseBody, provider);
3755
+ if (usage && typeof usage === "object") {
3756
+ attachCompressionUsageReceiptAfterAnalytics(usage as Record<string, unknown>, "provider");
3757
+ }
3758
+
3759
+ // Context Editing telemetry: when the delegated server-side clear actually ran,
3760
+ // record the provider's cleared-token receipt under engine "context-editing" so
3761
+ // it surfaces in compression analytics. Best-effort, Claude-only, non-streaming.
3762
+ recordContextEditingTelemetryHook({
3763
+ contextEditingEnabled,
3764
+ provider,
3765
+ responseBody,
3766
+ skillRequestId,
3767
+ log,
3768
+ });
3769
+ appendRequestLog({
3770
+ model,
3771
+ provider,
3772
+ connectionId: successConnectionId,
3773
+ tokens: usage,
3774
+ status: "200 OK",
3775
+ }).catch(() => {});
3776
+
3777
+ // Save structured call log with full payloads
3778
+ const cacheUsageLogMeta = buildCacheUsageLogMeta(usage);
3779
+ recordNonStreamingUsageStats(usage, {
3780
+ traceEnabled,
3781
+ provider,
3782
+ connectionId: successConnectionId,
3783
+ model,
3784
+ startTime,
3785
+ apiKeyInfo,
3786
+ effectiveServiceTier,
3787
+ isCombo,
3788
+ comboStrategy,
3789
+ endpoint: endpointPath,
3790
+ });
3791
+
3792
+ // Translate response to client's expected format (usually OpenAI)
3793
+ // Pass toolNameMap so Claude OAuth proxy_ prefix is stripped in tool_use blocks (#605)
3794
+ let translatedResponse = needsTranslation(responsePayloadFormat, clientResponseFormat)
3795
+ ? translateNonStreamingResponse(
3796
+ responseBody,
3797
+ responsePayloadFormat,
3798
+ clientResponseFormat,
3799
+ responseToolNameMap
3800
+ )
3801
+ : responseBody;
3802
+ const memoryExtractionResponse = translatedResponse;
3803
+
3804
+ // T26: Strip markdown code blocks if provider format is Claude
3805
+ if (sourceFormat === "claude" && !stream) {
3806
+ if (typeof translatedResponse?.choices?.[0]?.message?.content === "string") {
3807
+ translatedResponse.choices[0].message.content = stripMarkdownCodeFence(
3808
+ translatedResponse.choices[0].message.content
3809
+ ) as string;
3810
+ }
3811
+ }
3812
+
3813
+ // T18: Normalize finish_reason to 'tool_calls' if tool calls are present
3814
+ if (translatedResponse?.choices) {
3815
+ for (const choice of translatedResponse.choices) {
3816
+ if (
3817
+ choice.message?.tool_calls &&
3818
+ choice.message.tool_calls.length > 0 &&
3819
+ choice.finish_reason !== "tool_calls"
3820
+ ) {
3821
+ choice.finish_reason = "tool_calls";
3822
+ }
3823
+ }
3824
+ }
3825
+
3826
+ // Reasoning Replay Cache (#1628): Capture reasoning_content from non-streaming responses
3827
+ // with tool_calls so it can be replayed on subsequent turns (DeepSeek V4, Kimi K2, etc.)
3828
+ try {
3829
+ const firstChoice = translatedResponse?.choices?.[0];
3830
+ const msg = firstChoice?.message;
3831
+ cacheReasoningFromAssistantMessage(msg, provider, model, {
3832
+ requestId: skillRequestId,
3833
+ messageIndex: 0,
3834
+ });
3835
+ } catch {
3836
+ // Cache capture is non-critical — never block the response
3837
+ }
3838
+ // Sanitize response for OpenAI SDK compatibility
3839
+ // Strips non-standard fields (x_groq, usage_breakdown, service_tier, etc.)
3840
+ // Extracts <think> and <thinking> tags into reasoning_content
3841
+ // Source format determines output shape. If we are outputting OpenAI shape or pseudo-OpenAI shape, sanitize.
3842
+ if (clientResponseFormat === FORMATS.OPENAI_RESPONSES) {
3843
+ translatedResponse = sanitizeResponsesApiResponse(translatedResponse);
3844
+ } else if (clientResponseFormat === FORMATS.OPENAI) {
3845
+ // Port of decolua/9router#517: opt-in `x-omniroute-strip-reasoning` header
3846
+ // unconditionally drops `reasoning_content` from the final non-streaming
3847
+ // JSON for clients (e.g. Firecrawl AI SDK) whose JSON parsers break on
3848
+ // that non-standard field. Reasoning replay cache is captured above this
3849
+ // sanitize step, so the cache feature is unaffected.
3850
+ const stripReasoning = isStripReasoningRequested(clientRawRequest?.headers ?? null);
3851
+ translatedResponse = sanitizeOpenAIResponse(translatedResponse, {
3852
+ stripReasoning,
3853
+ parseTextualReasoningTags: shouldParseTextualReasoningTags(provider, model),
3854
+ });
3855
+ }
3856
+
3857
+ applyClientUsageBuffer(translatedResponse, body, clientResponseFormat);
3858
+
3859
+ if (memoryOwnerId && memorySettings?.enabled && memorySettings.maxTokens > 0) {
3860
+ const requestMemoryText = extractMemoryTextFromRequestBody(body as Record<string, unknown>);
3861
+ if (requestMemoryText) {
3862
+ extractFacts(requestMemoryText, memoryOwnerId, pipelineSessionId);
3863
+ }
3864
+
3865
+ const memoryText = extractMemoryTextFromResponse(memoryExtractionResponse);
3866
+ if (memoryText) {
3867
+ extractFacts(memoryText, memoryOwnerId, pipelineSessionId);
3868
+ }
3869
+ }
3870
+
3871
+ const customSkillExecutionEnabled =
3872
+ Boolean(memoryOwnerId) && memorySettings?.skillsEnabled === true;
3873
+ const builtinToolNames = webSearchFallbackPlan.toolName ? [webSearchFallbackPlan.toolName] : [];
3874
+ if (customSkillExecutionEnabled || builtinToolNames.length > 0) {
3875
+ const skillSessionId = pipelineSessionId;
3876
+
3877
+ translatedResponse = await handleToolCallExecution(
3878
+ translatedResponse,
3879
+ getSkillsModelIdForFormat(sourceFormat),
3880
+ {
3881
+ apiKeyId: memoryOwnerId || "local",
3882
+ sessionId: skillSessionId,
3883
+ requestId: skillRequestId,
3884
+ builtinToolNames,
3885
+ customSkillExecutionEnabled,
3886
+ }
3887
+ );
3888
+ }
3889
+
3890
+ const guardrailContext = buildPostCallGuardrailContext({
3891
+ apiKeyInfo,
3892
+ body,
3893
+ clientRawRequest,
3894
+ log,
3895
+ model,
3896
+ provider,
3897
+ responsePayloadFormat,
3898
+ clientResponseFormat,
3899
+ });
3900
+ const postCallGuardrails = await guardrailRegistry.runPostCallHooks(
3901
+ translatedResponse,
3902
+ guardrailContext
3903
+ );
3904
+ translatedResponse = postCallGuardrails.response;
3905
+
3906
+ const responseUsage =
3907
+ (usage && typeof usage === "object" ? usage : null) ||
3908
+ (translatedResponse?.usage && typeof translatedResponse.usage === "object"
3909
+ ? translatedResponse.usage
3910
+ : null);
3911
+ const estimatedCost = responseUsage
3912
+ ? await calculateCost(provider, model, responseUsage, { serviceTier: effectiveServiceTier })
3913
+ : 0;
3914
+
3915
+ if (postCallGuardrails.blocked) {
3916
+ const guardrailMessage = postCallGuardrails.message || "Response blocked by guardrail";
3917
+ persistAttemptLogs({
3918
+ status: HTTP_STATUS.BAD_REQUEST,
3919
+ tokens: usage,
3920
+ responseBody,
3921
+ providerRequest: finalBody || translatedBody,
3922
+ providerResponse: looksLikeSSE
3923
+ ? {
3924
+ _streamed: true,
3925
+ _format: "sse-json",
3926
+ summary: responseBody,
3927
+ }
3928
+ : responseBody,
3929
+ clientResponse: buildErrorBody(HTTP_STATUS.BAD_REQUEST, guardrailMessage),
3930
+ claudeCacheMeta: claudePromptCacheLogMeta,
3931
+ claudeCacheUsageMeta: cacheUsageLogMeta,
3932
+ cacheSource: "upstream",
3933
+ });
3934
+ if (apiKeyInfo?.id && estimatedCost > 0) {
3935
+ recordCost(apiKeyInfo.id, estimatedCost);
3936
+ }
3937
+ log?.warn?.(
3938
+ "GUARDRAIL",
3939
+ `Response blocked by ${postCallGuardrails.guardrail || "guardrail"}: ${guardrailMessage}`
3940
+ );
3941
+ finalizePendingScope(pendingScope, {
3942
+ providerResponse: responseBody,
3943
+ clientResponse: translatedResponse,
3944
+ });
3945
+ return createErrorResult(HTTP_STATUS.BAD_REQUEST, guardrailMessage);
3946
+ }
3947
+
3948
+ // Validate the *translated* response actually carries client-usable output.
3949
+ // isEmptyContentResponse (above) runs on the raw responseBody before translation;
3950
+ // this check runs after translation + sanitization + tool-call execution to catch
3951
+ // cases where a provider returns a structurally valid raw body that translates into
3952
+ // choices:[] or output:[] with no usable content (Responses API shape included).
3953
+ const malformedTranslatedReason = detectMalformedNonStream(translatedResponse);
3954
+ if (malformedTranslatedReason) {
3955
+ const totalLatency = Date.now() - startTime;
3956
+ const rawBytes = (() => {
3957
+ try {
3958
+ return JSON.stringify(responseBody || {}).length;
3959
+ } catch {
3960
+ return -1;
3961
+ }
3962
+ })();
3963
+ reportMalformed200({
3964
+ mode: "nonstream",
3965
+ provider,
3966
+ model,
3967
+ connectionId,
3968
+ reason: malformedTranslatedReason,
3969
+ recvBytes: rawBytes,
3970
+ recvLines: -1,
3971
+ emitted: -1,
3972
+ events: {},
3973
+ ttftMs: totalLatency,
3974
+ elapsedMs: totalLatency,
3975
+ });
3976
+ appendRequestLog({
3977
+ model,
3978
+ provider,
3979
+ connectionId,
3980
+ status: `FAILED ${HTTP_STATUS.BAD_GATEWAY}`,
3981
+ }).catch(() => {});
3982
+ const malformedMessage = `[${provider}/${model}] returned an empty response (no usable choices/output)`;
3983
+ persistAttemptLogs({
3984
+ status: HTTP_STATUS.BAD_GATEWAY,
3985
+ tokens: usage,
3986
+ responseBody,
3987
+ providerRequest: finalBody || translatedBody,
3988
+ providerResponse: looksLikeSSE
3989
+ ? { _streamed: true, _format: "sse-json", summary: responseBody }
3990
+ : responseBody,
3991
+ clientResponse: buildErrorBody(HTTP_STATUS.BAD_GATEWAY, malformedMessage),
3992
+ claudeCacheMeta: claudePromptCacheLogMeta,
3993
+ claudeCacheUsageMeta: cacheUsageLogMeta,
3994
+ cacheSource: "upstream",
3995
+ });
3996
+ persistFailureUsage(HTTP_STATUS.BAD_GATEWAY, "malformed_translated_response");
3997
+ trackPendingRequest(model, provider, pendingConnId, false);
3998
+ return createErrorResult(HTTP_STATUS.BAD_GATEWAY, malformedMessage);
3999
+ }
4000
+
4001
+ // ── Phase 9.1: Cache store (non-streaming, temp=0) ──
4002
+ storeSemanticCacheResponse({
4003
+ enabled: semanticCacheEnabled,
4004
+ body,
4005
+ headers: clientRawRequest?.headers,
4006
+ translatedResponse,
4007
+ model,
4008
+ apiKeyId: apiKeyInfo?.id ?? undefined,
4009
+ usage,
4010
+ log,
4011
+ });
4012
+
4013
+ // ── Phase 9.2: Save for idempotency ──
4014
+ // Reuse the key resolved by checkIdempotencyCache() above (single derivation per
4015
+ // request). (#3821-review LEDGER-6)
4016
+ saveIdempotency(idempotencyKey, translatedResponse, 200);
4017
+ reqLogger.logConvertedResponse(translatedResponse);
4018
+ persistAttemptLogs({
4019
+ status: 200,
4020
+ tokens: usage,
4021
+ responseBody,
4022
+ providerRequest: finalBody || translatedBody,
4023
+ providerResponse: looksLikeSSE
4024
+ ? {
4025
+ _streamed: true,
4026
+ _format: "sse-json",
4027
+ summary: responseBody,
4028
+ }
4029
+ : responseBody,
4030
+ clientResponse: translatedResponse,
4031
+ claudeCacheMeta: claudePromptCacheLogMeta,
4032
+ claudeCacheUsageMeta: cacheUsageLogMeta,
4033
+ cacheSource: "upstream",
4034
+ });
4035
+ if (apiKeyInfo?.id && estimatedCost > 0) {
4036
+ recordCost(apiKeyInfo.id, estimatedCost);
4037
+ }
4038
+
4039
+ // === Quota Share POST-hook (B/F7) — fire-and-forget, fail-open ===
4040
+ await scheduleQuotaShareConsumption({
4041
+ apiKeyId: apiKeyInfo?.id,
4042
+ connectionId: credentials?.connectionId,
4043
+ provider,
4044
+ model,
4045
+ usage,
4046
+ estimatedCost,
4047
+ log,
4048
+ });
4049
+ // === /Quota Share POST-hook ===
4050
+
4051
+ // ── Gamification event (fire-and-forget) ──
4052
+ await emitRequestGamificationEvent({ apiKeyId: apiKeyInfo?.id, model, provider });
4053
+
4054
+ finalizePendingScope(pendingScope, {
4055
+ providerResponse: responseBody,
4056
+ clientResponse: translatedResponse,
4057
+ });
4058
+ const responseHeaders = buildNonStreamingResponseHeaders({
4059
+ provider,
4060
+ model,
4061
+ startTime,
4062
+ responseUsage,
4063
+ estimatedCost,
4064
+ requestId: skillRequestId,
4065
+ compressionResponseMeta,
4066
+ });
4067
+ // #6426: align response body `model` with the `X-OmniRoute-Model` header
4068
+ // (both must be the resolved backend model). Some upstreams (notably legacy
4069
+ // /v1/completions text-completion path) return a body `model` field that
4070
+ // differs from the resolved backend id we advertised in the header, leaving
4071
+ // strict clients unable to reconcile the two. Rewrite body.model to `model`
4072
+ // FIRST, then let #1311 echo override it when the opt-in setting is on.
4073
+ if (typeof model === "string" && model) echoModelInObject(translatedResponse, model);
4074
+ // #1311: echo the requested alias/combo name in the non-streaming response model.
4075
+ if (echoModel) echoModelInObject(translatedResponse, echoModel);
4076
+ return {
4077
+ success: true,
4078
+ response: buildNonStreamingJsonResponse(translatedResponse, responseHeaders),
4079
+ };
4080
+ }
4081
+
4082
+ // Streaming response
4083
+ // #3089 — some "reasoning" openai-compatible upstreams ignore a stream:true
4084
+ // request and return a complete application/json chat-completion body instead
4085
+ // of an SSE stream. The readiness check below only recognizes SSE `data:`
4086
+ // frames, so that body produced a spurious STREAM_EARLY_EOF / HTTP 502 even
4087
+ // though it carried valid content/reasoning_content. Detect a JSON (non-SSE)
4088
+ // upstream body and synthesize an equivalent OpenAI SSE stream so the
4089
+ // streaming pipeline (and the client) get a valid stream.
4090
+ providerResponse = await maybeConvertJsonBodyToSse(providerResponse, { log, provider, model });
4091
+ const streamReadinessPolicy = resolveStreamReadinessTimeout({
4092
+ baseTimeoutMs: STREAM_READINESS_TIMEOUT_MS,
4093
+ provider,
4094
+ model,
4095
+ body: (finalBody || translatedBody) as Record<string, unknown> | null | undefined,
4096
+ maxTimeoutMs: agentGoalPolicy.detected
4097
+ ? Math.max(STREAM_READINESS_MAX_TIMEOUT_MS, agentGoalPolicy.readinessMaxTimeoutMs)
4098
+ : STREAM_READINESS_MAX_TIMEOUT_MS,
4099
+ });
4100
+ if (streamReadinessPolicy.timeoutMs !== streamReadinessPolicy.baseTimeoutMs) {
4101
+ log?.debug?.(
4102
+ "STREAM",
4103
+ `adaptive readiness timeout=${streamReadinessPolicy.timeoutMs}ms base=${streamReadinessPolicy.baseTimeoutMs}ms reason=${streamReadinessPolicy.reasons.join(",")}`
4104
+ );
4105
+ }
4106
+
4107
+ const streamReadiness = await ensureStreamReadiness(providerResponse, {
4108
+ timeoutMs: streamReadinessPolicy.timeoutMs,
4109
+ provider,
4110
+ model,
4111
+ log,
4112
+ });
4113
+ if (streamReadiness.ok === false) {
4114
+ const { response: failureResponse, reason } = streamReadiness;
4115
+ const failure = {
4116
+ status: failureResponse.status,
4117
+ message: reason,
4118
+ code: streamReadiness.code,
4119
+ type: streamReadiness.type,
4120
+ };
4121
+ trackPendingRequest(model, provider, connectionId, false);
4122
+ appendRequestLog({
4123
+ model,
4124
+ provider,
4125
+ connectionId,
4126
+ status: `FAILED ${failureResponse.status}`,
4127
+ }).catch(() => {});
4128
+ persistAttemptLogs({
4129
+ status: failureResponse.status,
4130
+ error: reason,
4131
+ providerRequest: finalBody || translatedBody,
4132
+ clientResponse: buildErrorBody(failureResponse.status, reason),
4133
+ claudeCacheMeta: claudePromptCacheLogMeta,
4134
+ cacheSource: "upstream",
4135
+ });
4136
+ persistFailureUsage(failureResponse.status, streamReadiness.code);
4137
+ // Do NOT call onStreamFailure — a stream stall is an upstream issue,
4138
+ // not an account/quota failure. Marking the account unavailable here
4139
+ // would lock out legitimate accounts when the upstream hangs.
4140
+ return {
4141
+ success: false,
4142
+ status: failureResponse.status,
4143
+ error: reason,
4144
+ errorType: streamReadiness.type,
4145
+ errorCode: streamReadiness.code,
4146
+ response: failureResponse,
4147
+ };
4148
+ }
4149
+ providerResponse = streamReadiness.response;
4150
+
4151
+ // Notify success - caller can clear error status if needed
4152
+ if (onRequestSuccess) {
4153
+ await onRequestSuccess();
4154
+ }
4155
+
4156
+ const responseHeaders = assembleStreamingResponseHeaders({
4157
+ providerHeaders: providerResponse.headers,
4158
+ provider,
4159
+ model,
4160
+ pendingRequestId,
4161
+ compressionResponseMeta,
4162
+ });
4163
+
4164
+ // Create transform stream with logger for streaming response
4165
+ let transformStream;
4166
+ const responseToolNameMap = mergeResponseToolNameMap(
4167
+ toolNameMap,
4168
+ (finalBody as Record<string, unknown> | null | undefined) ?? null
4169
+ );
4170
+
4171
+ let streamCompletionRecorded = false;
4172
+ let streamFailureCompletionRecorded = false;
4173
+
4174
+ // Callback to save call log when stream completes (include responseBody when provided by stream)
4175
+ const onStreamComplete = ({
4176
+ status: streamStatus,
4177
+ usage: streamUsage,
4178
+ responseBody: streamResponseBody,
4179
+ providerPayload,
4180
+ clientPayload,
4181
+ error: streamError,
4182
+ errorCode: streamErrorCode,
4183
+ ttft,
4184
+ }) => {
4185
+ const normalizedStreamStatus = streamStatus || 200;
4186
+ if (streamCompletionRecorded) return;
4187
+ streamCompletionRecorded = true;
4188
+ if (normalizedStreamStatus !== 200) {
4189
+ if (streamFailureCompletionRecorded) return;
4190
+ streamFailureCompletionRecorded = true;
4191
+ }
4192
+ const cacheUsageLogMeta = buildCacheUsageLogMeta(streamUsage);
4193
+ const streamConnectionId = getCurrentConnectionId();
4194
+
4195
+ if (normalizedStreamStatus === 200) {
4196
+ void maybeSyncClaudeExtraUsageState({
4197
+ provider,
4198
+ connectionId: streamConnectionId,
4199
+ providerSpecificData: credentials?.providerSpecificData,
4200
+ log,
4201
+ });
4202
+ }
4203
+
4204
+ // Reasoning Replay Cache (#1628): Capture reasoning_content from streaming responses
4205
+ // with tool_calls so it can be replayed on subsequent turns (DeepSeek V4, Kimi K2, etc.)
4206
+ if (normalizedStreamStatus === 200 && streamResponseBody) {
4207
+ try {
4208
+ const body = streamResponseBody as Record<string, unknown>;
4209
+ const choices = body.choices as { message?: Record<string, unknown> }[] | undefined;
4210
+ const msg = choices?.[0]?.message;
4211
+ cacheReasoningFromAssistantMessage(msg, provider, model, {
4212
+ requestId: skillRequestId,
4213
+ messageIndex: 0,
4214
+ });
4215
+ } catch {
4216
+ // Cache capture is non-critical — never block the stream
4217
+ }
4218
+ }
4219
+ effectiveServiceTier = resolveReportedServiceTier(streamResponseBody) ?? effectiveServiceTier;
4220
+
4221
+ // Context Editing telemetry (streaming): the reconstructed stream body now carries
4222
+ // context_management.applied_edits from the final message_delta snapshot. Mirror the
4223
+ // non-streaming hook so streaming context-clear savings also surface under engine
4224
+ // "context-editing" in compression analytics. Best-effort, Claude-only.
4225
+ if (normalizedStreamStatus === 200) {
4226
+ recordContextEditingTelemetryHook({
4227
+ contextEditingEnabled,
4228
+ provider,
4229
+ responseBody: streamResponseBody,
4230
+ skillRequestId,
4231
+ log,
4232
+ });
4233
+ }
4234
+
4235
+ streamFailure.finalizeStreamRequestLog({
4236
+ pendingRequestId,
4237
+ model,
4238
+ provider,
4239
+ connectionId: streamConnectionId,
4240
+ providerResponse: providerPayload ?? streamResponseBody ?? undefined,
4241
+ clientResponse: clientPayload ?? streamResponseBody ?? undefined,
4242
+ status: normalizedStreamStatus,
4243
+ error: streamError,
4244
+ errorCode: streamErrorCode,
4245
+ });
4246
+
4247
+ // Track cache token metrics for streaming responses
4248
+ if (streamUsage && typeof streamUsage === "object") {
4249
+ attachCompressionUsageReceiptAfterAnalytics(streamUsage as Record<string, unknown>, "stream");
4250
+ }
4251
+ recordStreamingUsageStats(streamUsage, {
4252
+ provider,
4253
+ model,
4254
+ streamStatus: normalizedStreamStatus,
4255
+ startTime,
4256
+ ttft,
4257
+ streamErrorCode,
4258
+ connectionId: streamConnectionId,
4259
+ apiKeyInfo,
4260
+ effectiveServiceTier,
4261
+ isCombo,
4262
+ comboStrategy,
4263
+ endpoint: endpointPath,
4264
+ });
4265
+
4266
+ persistAttemptLogs({
4267
+ status: normalizedStreamStatus,
4268
+ error: streamError || undefined,
4269
+ tokens: streamUsage || {},
4270
+ responseBody: streamResponseBody ?? undefined,
4271
+ providerRequest: finalBody || translatedBody,
4272
+ providerResponse: providerPayload,
4273
+ clientResponse: clientPayload ?? streamResponseBody ?? undefined,
4274
+ claudeCacheMeta: claudePromptCacheLogMeta,
4275
+ claudeCacheUsageMeta: cacheUsageLogMeta,
4276
+ cacheSource: "upstream",
4277
+ });
4278
+
4279
+ recordStreamingCost({
4280
+ apiKeyId: apiKeyInfo?.id,
4281
+ provider,
4282
+ model,
4283
+ streamUsage,
4284
+ serviceTier: effectiveServiceTier,
4285
+ calculateCost,
4286
+ recordCost,
4287
+ });
4288
+
4289
+ // === Quota Share POST-hook streaming (B/F7) — fire-and-forget, fail-open ===
4290
+ // Resolve the real per-request cost (calculateCost) so USD-unit pools accrue
4291
+ // on streaming traffic too; this previously recorded usd:0 hardcoded, which
4292
+ // meant DeepSeek-style `usd/monthly` shared pools never blocked on streams.
4293
+ scheduleStreamingQuotaShareConsumption({
4294
+ apiKeyId: apiKeyInfo?.id,
4295
+ connectionId: credentials?.connectionId,
4296
+ provider,
4297
+ model,
4298
+ streamUsage,
4299
+ streamStatus: normalizedStreamStatus,
4300
+ serviceTier: effectiveServiceTier,
4301
+ calculateCost,
4302
+ log,
4303
+ });
4304
+ // === /Quota Share POST-hook streaming ===
4305
+
4306
+ if (
4307
+ memoryOwnerId &&
4308
+ memorySettings?.enabled &&
4309
+ memorySettings.maxTokens > 0 &&
4310
+ streamStatus === 200
4311
+ ) {
4312
+ const requestMemoryText = extractMemoryTextFromRequestBody(body as Record<string, unknown>);
4313
+ if (requestMemoryText) {
4314
+ extractFacts(requestMemoryText, memoryOwnerId, pipelineSessionId);
4315
+ }
4316
+
4317
+ const streamedMemoryText = extractMemoryTextFromResponse(
4318
+ (streamResponseBody ?? null) as Record<string, unknown> | null
4319
+ );
4320
+ if (streamedMemoryText) {
4321
+ extractFacts(streamedMemoryText, memoryOwnerId, pipelineSessionId);
4322
+ }
4323
+ }
4324
+
4325
+ // Semantic cache: store assembled streaming response for future cache hits
4326
+ storeStreamingSemanticCacheResponse({
4327
+ enabled: semanticCacheEnabled,
4328
+ streamStatus,
4329
+ streamResponseBody,
4330
+ body,
4331
+ headers: clientRawRequest?.headers,
4332
+ model,
4333
+ apiKeyId: apiKeyInfo?.id ?? undefined,
4334
+ streamUsage,
4335
+ log,
4336
+ });
4337
+ };
4338
+
4339
+ const streamFailureFinalizers = streamFailure.createStreamFailureFinalizers({
4340
+ isFailureCompletionRecorded: () => streamFailureCompletionRecorded,
4341
+ isStreamCompletionRecorded: () => streamCompletionRecorded,
4342
+ onStreamComplete,
4343
+ persistFailureUsage,
4344
+ onStreamFailure,
4345
+ });
4346
+ const handleStreamFailure = streamFailureFinalizers.handleStreamFailure;
4347
+ onPipelineStreamError = streamFailureFinalizers.onPipelineStreamError;
4348
+ onClientDisconnectFinalize = (event) =>
4349
+ handleStreamFailure({
4350
+ status: 499,
4351
+ message: `Client disconnected: ${event.reason}`,
4352
+ code: "client_disconnected",
4353
+ type: "client_disconnected",
4354
+ });
4355
+
4356
+ // For providers using Responses API format, translate stream back to openai (Chat Completions) format
4357
+ // UNLESS client is Droid CLI which expects openai-responses format back
4358
+ const needsResponsesTranslation =
4359
+ targetFormat === FORMATS.OPENAI_RESPONSES &&
4360
+ clientResponseFormat === FORMATS.OPENAI &&
4361
+ !isResponsesEndpoint &&
4362
+ !isDroidCLI;
4363
+ const streamStateBody = finalBody || body;
4364
+
4365
+ if (needsResponsesTranslation) {
4366
+ // Provider returns openai-responses, translate to openai (Chat Completions) that clients expect
4367
+ log?.debug?.("STREAM", `Responses translation mode: openai-responses → openai`);
4368
+ transformStream = createSSETransformStreamWithLogger(
4369
+ "openai-responses",
4370
+ "openai",
4371
+ provider,
4372
+ reqLogger,
4373
+ responseToolNameMap,
4374
+ model,
4375
+ connectionId,
4376
+ streamStateBody,
4377
+ onStreamComplete,
4378
+ apiKeyInfo,
4379
+ handleStreamFailure,
4380
+ copilotCompatibleReasoning
4381
+ );
4382
+ } else if (needsTranslation(targetFormat, clientResponseFormat)) {
4383
+ // Standard translation for other providers
4384
+ log?.debug?.("STREAM", `Translation mode: ${targetFormat} → ${clientResponseFormat}`);
4385
+ transformStream = createSSETransformStreamWithLogger(
4386
+ targetFormat,
4387
+ clientResponseFormat,
4388
+ provider,
4389
+ reqLogger,
4390
+ responseToolNameMap,
4391
+ model,
4392
+ connectionId,
4393
+ streamStateBody,
4394
+ onStreamComplete,
4395
+ apiKeyInfo,
4396
+ handleStreamFailure,
4397
+ copilotCompatibleReasoning,
4398
+ // Suppress the `</think>` close marker for clients that render it verbatim
4399
+ // (e.g. OpenCode by UA; any client via `x-omniroute-thinking-marker: off`);
4400
+ // preserved for Claude Code / Cursor and unknown clients by default (#5245 /
4401
+ // #5312). The header wins over the UA allowlist.
4402
+ resolveSuppressThinkClose({
4403
+ userAgent: streamUserAgent,
4404
+ thinkingMarkerHeader,
4405
+ })
4406
+ );
4407
+ } else {
4408
+ log?.debug?.("STREAM", `Standard passthrough mode`);
4409
+ transformStream = createPassthroughStreamWithLogger(
4410
+ provider,
4411
+ reqLogger,
4412
+ responseToolNameMap,
4413
+ model,
4414
+ connectionId,
4415
+ streamStateBody,
4416
+ onStreamComplete,
4417
+ apiKeyInfo,
4418
+ handleStreamFailure,
4419
+ clientResponseFormat
4420
+ );
4421
+ }
4422
+
4423
+ const finalStream = assembleStreamingPipeline({
4424
+ providerResponse,
4425
+ transformStream,
4426
+ streamController,
4427
+ createPiiTransform,
4428
+ clientRawRequestHeaders: clientRawRequest?.headers,
4429
+ clientResponseFormat,
4430
+ echoModel,
4431
+ responseHeaders,
4432
+ });
4433
+
4434
+ // ── Gamification event (fire-and-forget) ──
4435
+ await emitRequestGamificationEvent({ apiKeyId: apiKeyInfo?.id, model, provider });
4436
+
4437
+ // ── Plugin onResponse hook (fire-and-forget) ──
4438
+ await runPluginOnResponseHook({ requestId: traceId, body, model, provider, apiKeyInfo });
4439
+
4440
+ return {
4441
+ success: true,
4442
+ response: new Response(finalStream, {
4443
+ headers: responseHeaders,
4444
+ }),
4445
+ };
4446
+ }
4447
+
4448
+ export function isTokenExpiringSoon(expiresAt, bufferMs = 5 * 60 * 1000) {
4449
+ if (!expiresAt) return false;
4450
+ const expiresAtMs = new Date(expiresAt).getTime();
4451
+ return expiresAtMs - Date.now() < bufferMs;
4452
+ }