cartethyia 2.7.3 → 2.7.5

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 (625) hide show
  1. package/.env.example +2 -0
  2. package/README.md +17 -5
  3. package/dist/dashboard/assets/{BannedPage-2TJD37Hb.js → BannedPage-Cj15GsMp.js} +1 -1
  4. package/dist/dashboard/assets/BannedPage-Cj15GsMp.js.gz +0 -0
  5. package/dist/dashboard/assets/ClaimGiftsButton-D83pBJ6J.js +1 -0
  6. package/dist/dashboard/assets/ClaimGiftsButton-D83pBJ6J.js.gz +0 -0
  7. package/dist/dashboard/assets/{CliToolDetailPage-xefJNRmJ.js → CliToolDetailPage-DZKmVuJN.js} +1 -1
  8. package/dist/dashboard/assets/CliToolDetailPage-DZKmVuJN.js.gz +0 -0
  9. package/dist/dashboard/assets/{CliToolsPage-Rj9iHQ3F.js → CliToolsPage-BIccZmeL.js} +1 -1
  10. package/dist/dashboard/assets/CliToolsPage-BIccZmeL.js.gz +0 -0
  11. package/dist/dashboard/assets/CombosPage-DEEKkYD-.js +1 -0
  12. package/dist/dashboard/assets/CombosPage-DEEKkYD-.js.gz +0 -0
  13. package/dist/dashboard/assets/ConsoleLogPage-D5rol2j2.js +1 -0
  14. package/dist/dashboard/assets/ConsoleLogPage-D5rol2j2.js.gz +0 -0
  15. package/dist/dashboard/assets/CustomizationPage-BKj_r0rp.js +1 -0
  16. package/dist/dashboard/assets/CustomizationPage-BKj_r0rp.js.gz +0 -0
  17. package/dist/dashboard/assets/InjectSystemPromptPanel-B5nZvmf0.js +656 -0
  18. package/dist/dashboard/assets/InjectSystemPromptPanel-B5nZvmf0.js.gz +0 -0
  19. package/dist/dashboard/assets/{LoginPage-C4w7vea-.js → LoginPage-DkXb6dx-.js} +1 -1
  20. package/dist/dashboard/assets/LoginPage-DkXb6dx-.js.gz +0 -0
  21. package/dist/dashboard/assets/{ModelPicker-BtPcAfot.js → ModelPicker-DnQPSIA8.js} +1 -1
  22. package/dist/dashboard/assets/ModelPicker-DnQPSIA8.js.gz +0 -0
  23. package/dist/dashboard/assets/OverviewPage-Ci2wQKUz.js +1 -0
  24. package/dist/dashboard/assets/OverviewPage-Ci2wQKUz.js.gz +0 -0
  25. package/dist/dashboard/assets/ProviderDetailPage-odlgYrOg.js +5 -0
  26. package/dist/dashboard/assets/ProviderDetailPage-odlgYrOg.js.gz +0 -0
  27. package/dist/dashboard/assets/ProviderIcon-EfeloBn4.js +1 -0
  28. package/dist/dashboard/assets/ProviderIcon-EfeloBn4.js.gz +0 -0
  29. package/dist/dashboard/assets/ProvidersPage-CqFBQEB8.js +1 -0
  30. package/dist/dashboard/assets/ProvidersPage-CqFBQEB8.js.gz +0 -0
  31. package/dist/dashboard/assets/ProxyPage-5SnUSy3j.js +9 -0
  32. package/dist/dashboard/assets/ProxyPage-5SnUSy3j.js.gz +0 -0
  33. package/dist/dashboard/assets/QuotaPage-CmnMDzhP.js +1 -0
  34. package/dist/dashboard/assets/QuotaPage-CmnMDzhP.js.gz +0 -0
  35. package/dist/dashboard/assets/SettingsPage-BJtRZ4qD.js +1 -0
  36. package/dist/dashboard/assets/SettingsPage-BJtRZ4qD.js.gz +0 -0
  37. package/dist/dashboard/assets/{SetupPage-BQS0Rx_u.js → SetupPage-WTa0sktC.js} +1 -1
  38. package/dist/dashboard/assets/SetupPage-WTa0sktC.js.gz +0 -0
  39. package/dist/dashboard/assets/{StudioPage-TP7voAQ0.js → StudioPage-BfAb5-o_.js} +3 -3
  40. package/dist/dashboard/assets/StudioPage-BfAb5-o_.js.gz +0 -0
  41. package/dist/dashboard/assets/UsagePage-BNzeZ2Vk.js +56 -0
  42. package/dist/dashboard/assets/UsagePage-BNzeZ2Vk.js.gz +0 -0
  43. package/dist/dashboard/assets/api-keys-l29JROPv.js +1 -0
  44. package/dist/dashboard/assets/api-keys-l29JROPv.js.gz +0 -0
  45. package/dist/dashboard/assets/app--PJWq8_C.css +2 -0
  46. package/dist/dashboard/assets/app--PJWq8_C.css.gz +0 -0
  47. package/dist/dashboard/assets/{assert-C0YYuZJ4.js → assert-C0K-c4eo.js} +1 -1
  48. package/dist/dashboard/assets/assert-C0K-c4eo.js.gz +0 -0
  49. package/dist/dashboard/assets/{badge-B_DMpMJN.js → badge-DfbhQU4k.js} +1 -1
  50. package/dist/dashboard/assets/badge-DfbhQU4k.js.gz +0 -0
  51. package/dist/dashboard/assets/{banner-DMrMPmth.js → banner-BllT4LcF.js} +1 -1
  52. package/dist/dashboard/assets/banner-BllT4LcF.js.gz +0 -0
  53. package/dist/dashboard/assets/brand/login_register.jpg +0 -0
  54. package/dist/dashboard/assets/brand/login_register_initial.jpg +0 -0
  55. package/dist/dashboard/assets/brand/og_bansos.jpg +0 -0
  56. package/dist/dashboard/assets/brand/og_image.jpg +0 -0
  57. package/dist/dashboard/assets/{card-Be5Uzamx.js → card-CtkX66Wd.js} +1 -1
  58. package/dist/dashboard/assets/card-CtkX66Wd.js.gz +0 -0
  59. package/dist/dashboard/assets/chevron-left-Bf6CaTSJ.js +1 -0
  60. package/dist/dashboard/assets/chevron-left-Bf6CaTSJ.js.gz +0 -0
  61. package/dist/dashboard/assets/{cli-tools-muFRphDu.js → cli-tools-CQXVMn4P.js} +1 -1
  62. package/dist/dashboard/assets/cli-tools-CQXVMn4P.js.gz +0 -0
  63. package/dist/dashboard/assets/{clipboard-button-DOfrVmsi.js → clipboard-button-BQqBQd2n.js} +1 -1
  64. package/dist/dashboard/assets/clipboard-button-BQqBQd2n.js.gz +0 -0
  65. package/dist/dashboard/assets/{dialog-C9omdjQt.js → dialog-BEK7KP0H.js} +1 -1
  66. package/dist/dashboard/assets/dialog-BEK7KP0H.js.gz +0 -0
  67. package/dist/dashboard/assets/{drawer-Bm8WKaGY.js → drawer-BrTEZ8zu.js} +1 -1
  68. package/dist/dashboard/assets/drawer-BrTEZ8zu.js.gz +0 -0
  69. package/dist/dashboard/assets/entry-CHWxbS56.js +1 -0
  70. package/dist/dashboard/assets/entry-CHWxbS56.js.gz +0 -0
  71. package/dist/dashboard/assets/{entry-q5HwZ8nN.js → entry-EBHJ3DrE.js} +1 -1
  72. package/dist/dashboard/assets/entry-EBHJ3DrE.js.gz +0 -0
  73. package/dist/dashboard/assets/entry-LCS85hiq.js +4 -0
  74. package/dist/dashboard/assets/entry-LCS85hiq.js.gz +0 -0
  75. package/dist/dashboard/assets/{formatters-BTScrQpe.js → formatters-BAACA727.js} +1 -1
  76. package/dist/dashboard/assets/formatters-BAACA727.js.gz +0 -0
  77. package/dist/dashboard/assets/{github-badge-Orjuzemr.js → github-badge-YSCE4UC2.js} +1 -1
  78. package/dist/dashboard/assets/github-badge-YSCE4UC2.js.gz +0 -0
  79. package/dist/dashboard/assets/{index-cPrX5vn6.js → index-FQlj0HUT.js} +2 -2
  80. package/dist/dashboard/assets/index-FQlj0HUT.js.gz +0 -0
  81. package/dist/dashboard/assets/{inline-Dvux8w-0.js → inline-C4gU5iOL.js} +1 -1
  82. package/dist/dashboard/assets/inline-C4gU5iOL.js.gz +0 -0
  83. package/dist/dashboard/assets/{input-CU1Rgf3p.js → input-BHHX_Tmm.js} +1 -1
  84. package/dist/dashboard/assets/input-BHHX_Tmm.js.gz +0 -0
  85. package/dist/dashboard/assets/{layout-C_7A6H55.js → layout-CajrYUnD.js} +1 -1
  86. package/dist/dashboard/assets/{layout-C_7A6H55.js.gz → layout-CajrYUnD.js.gz} +0 -0
  87. package/dist/dashboard/assets/{list-row-CzFvogbj.js → list-row-Bx2I7XUe.js} +1 -1
  88. package/dist/dashboard/assets/list-row-Bx2I7XUe.js.gz +0 -0
  89. package/dist/dashboard/assets/live-DNNqRbj2.js +1 -0
  90. package/dist/dashboard/assets/live-DNNqRbj2.js.gz +0 -0
  91. package/dist/dashboard/assets/minimize-2-BTmMTK4o.js +1 -0
  92. package/dist/dashboard/assets/minimize-2-BTmMTK4o.js.gz +0 -0
  93. package/dist/dashboard/assets/{mutation-JJGQm8TD.js → mutation-Rr0TIvcj.js} +1 -1
  94. package/dist/dashboard/assets/mutation-Rr0TIvcj.js.gz +0 -0
  95. package/dist/dashboard/assets/network-C5UBACrJ.js +1 -0
  96. package/dist/dashboard/assets/network-C5UBACrJ.js.gz +0 -0
  97. package/dist/dashboard/assets/numbers-CUWAZK4J.js +2 -0
  98. package/dist/dashboard/assets/numbers-CUWAZK4J.js.gz +0 -0
  99. package/dist/dashboard/assets/pagination-Xsi9Qx04.js +1 -0
  100. package/dist/dashboard/assets/pagination-Xsi9Qx04.js.gz +0 -0
  101. package/dist/dashboard/assets/{query-keys-CLaNz8Xz.js → query-keys-BloKtaXJ.js} +1 -1
  102. package/dist/dashboard/assets/query-keys-BloKtaXJ.js.gz +0 -0
  103. package/dist/dashboard/assets/quota-DvQbD_sr.js +1 -0
  104. package/dist/dashboard/assets/quota-DvQbD_sr.js.gz +0 -0
  105. package/dist/dashboard/assets/{routing-BEH32QOC.js → routing-in0f_IfK.js} +1 -1
  106. package/dist/dashboard/assets/routing-in0f_IfK.js.gz +0 -0
  107. package/dist/dashboard/assets/{select-B3iTetHN.js → select-BLULNqTp.js} +2 -2
  108. package/dist/dashboard/assets/select-BLULNqTp.js.gz +0 -0
  109. package/dist/dashboard/assets/settings-CcGczj6I.js +1 -0
  110. package/dist/dashboard/assets/settings-CcGczj6I.js.gz +0 -0
  111. package/dist/dashboard/assets/shield-BMQNxORh.js +1 -0
  112. package/dist/dashboard/assets/shield-BMQNxORh.js.gz +0 -0
  113. package/dist/dashboard/assets/{stack-KrOrm7JZ.js → stack-D2lIR0tg.js} +1 -1
  114. package/dist/dashboard/assets/stack-D2lIR0tg.js.gz +0 -0
  115. package/dist/dashboard/assets/{state-DLS7IDMy.js → state-DTARjfiW.js} +1 -1
  116. package/dist/dashboard/assets/state-DTARjfiW.js.gz +0 -0
  117. package/dist/dashboard/assets/{switch-42ItvbAs.js → switch-c0oPSnqW.js} +1 -1
  118. package/dist/dashboard/assets/switch-c0oPSnqW.js.gz +0 -0
  119. package/dist/dashboard/assets/{useMutation-Dmnfh1Ue.js → useMutation-m8WGNPrz.js} +1 -1
  120. package/dist/dashboard/assets/useMutation-m8WGNPrz.js.gz +0 -0
  121. package/dist/dashboard/console-backgrounds.jpg +0 -0
  122. package/dist/dashboard/console-backgrounds_initial.jpg +0 -0
  123. package/dist/dashboard/index.html +2 -2
  124. package/dist/dashboard/index.html.gz +0 -0
  125. package/dist/dashboard/precompressed-assets.json +59 -57
  126. package/migrations/full-postgres/0027_qoder_identity_genuine_seed.sql +9 -10
  127. package/migrations/full-postgres/0032_add_provider_pool_accounts.sql +53 -0
  128. package/migrations/full-postgres/0033_add_api_key_ban.sql +9 -0
  129. package/migrations/full-postgres/0034_add_api_key_ban_until.sql +7 -0
  130. package/migrations/full-postgres/0035_align_pool_account_status_enum.sql +21 -0
  131. package/migrations/full-postgres/0036_system_prompt_presets_refs.sql +10 -0
  132. package/migrations/full-postgres/0037_share_concurrency_scope.sql +3 -0
  133. package/migrations/full-postgres/0038_add_accounts_quota_snapshot.sql +7 -0
  134. package/migrations/full-postgres/0039_add_telemetry_provider_model_indexes.sql +13 -0
  135. package/migrations/full-postgres/0040_add_models_dialect.sql +3 -0
  136. package/migrations/full-postgres/0041_add_daily_checkin_ledger_account_fk.sql +7 -0
  137. package/migrations/lite-sqlite3/0027_qoder_identity_genuine_seed.sql +9 -10
  138. package/migrations/lite-sqlite3/0032_add_provider_pool_accounts.sql +44 -0
  139. package/migrations/lite-sqlite3/0033_add_api_key_ban.sql +8 -0
  140. package/migrations/lite-sqlite3/0034_add_api_key_ban_until.sql +5 -0
  141. package/migrations/lite-sqlite3/0035_align_pool_account_status_enum.sql +6 -0
  142. package/migrations/lite-sqlite3/0036_system_prompt_presets_refs.sql +14 -0
  143. package/migrations/lite-sqlite3/0037_share_concurrency_scope.sql +3 -0
  144. package/migrations/lite-sqlite3/0038_add_accounts_quota_snapshot.sql +4 -0
  145. package/migrations/lite-sqlite3/0039_add_telemetry_provider_model_indexes.sql +6 -0
  146. package/migrations/lite-sqlite3/0040_add_models_dialect.sql +3 -0
  147. package/migrations/lite-sqlite3/0041_add_daily_checkin_ledger_account_fk.sql +26 -0
  148. package/package.json +4 -5
  149. package/scripts/cli/update.ts +15 -41
  150. package/src/boot/app.ts +67 -10
  151. package/src/boot/dependencies.ts +786 -732
  152. package/src/boot/lifecycle.ts +11 -12
  153. package/src/config/cooldowns.ts +34 -55
  154. package/src/config/network.ts +8 -4
  155. package/src/config/package-paths.ts +1 -1
  156. package/src/config/providers.ts +13 -14
  157. package/src/config/resources.ts +0 -5
  158. package/src/config/runtime.ts +2 -5
  159. package/src/config/spec.ts +26 -6
  160. package/src/console/api-keys/ban-constants.ts +38 -0
  161. package/src/console/api-keys/contracts.ts +92 -31
  162. package/src/console/api-keys/routes.ts +131 -6
  163. package/src/console/audit/routes.ts +3 -3
  164. package/src/console/auth/access.ts +4 -4
  165. package/src/console/auth/account-reset.ts +5 -5
  166. package/src/console/auth/audit-recorder.ts +1 -1
  167. package/src/console/auth/lockout.ts +7 -3
  168. package/src/console/auth/row-coercion.ts +57 -12
  169. package/src/console/auth/session-resolver.ts +1 -1
  170. package/src/console/auth/session-service.ts +8 -2
  171. package/src/console/auth/session.ts +40 -34
  172. package/src/console/auth/setup-service.ts +4 -3
  173. package/src/console/auth/user-store.ts +1 -1
  174. package/src/console/backup/9router-map.ts +0 -3
  175. package/src/console/backup/contracts.ts +11 -9
  176. package/src/console/backup/export-endpoint.ts +11 -12
  177. package/src/console/backup/nine-router.ts +1 -7
  178. package/src/console/backup/routes.ts +32 -36
  179. package/src/console/backup/service.ts +468 -468
  180. package/src/console/backup/store.ts +47 -29
  181. package/src/console/backup/validate.ts +11 -1
  182. package/src/console/cli-tools/contracts.ts +7 -7
  183. package/src/console/cli-tools/injectors/cline.ts +110 -110
  184. package/src/console/cli-tools/injectors/codex.ts +181 -181
  185. package/src/console/cli-tools/injectors/copilot.ts +108 -108
  186. package/src/console/cli-tools/injectors/driver.ts +247 -247
  187. package/src/console/cli-tools/injectors/jcode.ts +115 -115
  188. package/src/console/cli-tools/injectors/openclaw.ts +161 -161
  189. package/src/console/cli-tools/injectors/opencode.ts +153 -153
  190. package/src/console/cli-tools/routes.ts +4 -4
  191. package/src/console/cli-tools/service.ts +1 -1
  192. package/src/console/cli-tools/store.ts +2 -2
  193. package/src/console/console-router-guards.ts +2 -2
  194. package/src/console/console-router.ts +36 -4
  195. package/src/console/dashboard-assets.ts +9 -9
  196. package/src/console/data-management/routes.ts +3 -3
  197. package/src/console/domain-registration.ts +18 -4
  198. package/src/console/inferhub/client.ts +0 -7
  199. package/src/console/inferhub/key-store.ts +1 -1
  200. package/src/console/inferhub/normalize.ts +2 -2
  201. package/src/console/inferhub/routes.ts +1 -1
  202. package/src/console/inferhub/types.ts +1 -1
  203. package/src/console/observability/contracts.ts +5 -1
  204. package/src/console/observability/live.ts +34 -21
  205. package/src/console/observability/logs.ts +4 -4
  206. package/src/console/observability/performance.ts +3 -3
  207. package/src/console/observability/routes.ts +3 -3
  208. package/src/console/observability/sse.ts +62 -8
  209. package/src/console/observability/store.ts +36 -20
  210. package/src/console/providers/catalog/catalog-projections.ts +1 -1
  211. package/src/console/providers/catalog/contracts.ts +82 -22
  212. package/src/console/providers/catalog/model-operations.ts +1 -1
  213. package/src/console/providers/catalog/provider-operations.ts +110 -17
  214. package/src/console/providers/catalog/public-model-store.ts +0 -0
  215. package/src/console/providers/catalog/routes.ts +30 -6
  216. package/src/console/providers/catalog/store-helpers.ts +349 -326
  217. package/src/console/providers/catalog/store.ts +1662 -1292
  218. package/src/console/providers/detail/routes.ts +4 -4
  219. package/src/console/providers/oauth/callback-listener.ts +100 -18
  220. package/src/console/providers/oauth/routes.ts +95 -16
  221. package/src/console/quota/account-quota-global.ts +41 -3
  222. package/src/console/quota/account-quota-shared.ts +10 -3
  223. package/src/console/quota/account-quota-tenant.ts +285 -67
  224. package/src/console/quota/account-test-egress.ts +4 -4
  225. package/src/console/quota/daily-growth-job.ts +308 -0
  226. package/src/console/quota/native-identity.ts +2 -2
  227. package/src/console/quota/quota-view.ts +1 -1
  228. package/src/console/remote-support/routes.ts +1 -1
  229. package/src/console/remote-support/service.ts +2 -2
  230. package/src/console/routing/model/contracts.ts +1 -1
  231. package/src/console/routing/model/routes.ts +3 -3
  232. package/src/console/routing/pools/contracts.ts +13 -10
  233. package/src/console/routing/pools/import-parse.ts +3 -3
  234. package/src/console/routing/pools/operations.ts +14 -7
  235. package/src/console/routing/pools/probe-verdict.ts +8 -3
  236. package/src/console/routing/pools/relay-deploy.ts +12 -12
  237. package/src/console/routing/pools/routes.ts +3 -3
  238. package/src/console/routing/pools/store.ts +21 -1
  239. package/src/console/settings/contracts.ts +29 -2
  240. package/src/console/settings/routes.ts +3 -3
  241. package/src/console/settings/store.ts +20 -0
  242. package/src/console/share/share-router.ts +112 -44
  243. package/src/console/share/share-stats.ts +109 -39
  244. package/src/console/share/share-usage.ts +1 -1
  245. package/src/console/shared/errors.ts +2 -2
  246. package/src/console/shared/object.ts +18 -0
  247. package/src/console/shared/read-through-cache.ts +42 -6
  248. package/src/console/shared/transaction.ts +14 -0
  249. package/src/console/system-prompt/contracts.ts +89 -12
  250. package/src/console/system-prompt/routes.ts +98 -45
  251. package/src/console/updates/apply-update.ts +244 -0
  252. package/src/console/updates/routes.ts +256 -98
  253. package/src/main.ts +11 -5
  254. package/src/network/http2-bidi.ts +1 -1
  255. package/src/network/ip-utils.ts +298 -0
  256. package/src/network/outbound-fetch.ts +76 -5
  257. package/src/network/pool/agent.ts +7 -6
  258. package/src/network/pool/byte-accounting.ts +1 -1
  259. package/src/network/pool/resolver.ts +55 -51
  260. package/src/network/pool/selector.ts +150 -136
  261. package/src/network/pool-cooldown-store.ts +71 -16
  262. package/src/network/pool-health-machine.ts +22 -2
  263. package/src/network/rate-limit-verdict.ts +68 -0
  264. package/src/network/retry.ts +1 -1
  265. package/src/network/ssrf.ts +24 -75
  266. package/src/observability/event-log.ts +2 -2
  267. package/src/observability/event-query.ts +286 -214
  268. package/src/observability/log-sanitize.ts +1 -1
  269. package/src/observability/metrics.ts +4 -4
  270. package/src/observability/payload-capture.ts +6 -5
  271. package/src/observability/payload-store.ts +1 -1
  272. package/src/observability/runtime-metrics.ts +1 -1
  273. package/src/observability/telemetry-buffer.ts +12 -3
  274. package/src/observability/telemetry-reconciliation.ts +3 -3
  275. package/src/observability/telemetry-status.ts +1 -1
  276. package/src/observability/token-speed.ts +1 -1
  277. package/src/persistence/connection-url.ts +1 -1
  278. package/src/persistence/db-handle.ts +1 -1
  279. package/src/persistence/db-pg.ts +2 -2
  280. package/src/persistence/db-sqlite.ts +10 -5
  281. package/src/persistence/dialect.ts +5 -1
  282. package/src/persistence/readiness.ts +19 -7
  283. package/src/persistence/redis.ts +1 -1
  284. package/src/persistence/schema.ts +177 -45
  285. package/src/persistence/shared-cache.ts +2 -2
  286. package/src/persistence/sort-order.ts +1 -1
  287. package/src/persistence/stores/api-key-store.ts +109 -4
  288. package/src/persistence/stores/share-store.ts +20 -8
  289. package/src/persistence/stores/telemetry-store.ts +1 -1
  290. package/src/persistence/system-prompt-store.ts +63 -18
  291. package/src/persistence/tenant-preferences.ts +1 -1
  292. package/src/persistence/ttl-cache.ts +3 -3
  293. package/src/persistence/usage-flush.ts +7 -2
  294. package/src/protocol/canonical-model.ts +1 -1
  295. package/src/protocol/codecs.ts +12 -16
  296. package/src/protocol/dsml.ts +3 -49
  297. package/src/protocol/gateway-error.ts +6 -3
  298. package/src/protocol/image-clamp.ts +2 -2
  299. package/src/protocol/image-generation.ts +2 -2
  300. package/src/protocol/reasoning-text.ts +1 -1
  301. package/src/protocol/reasoning.ts +7 -52
  302. package/src/protocol/registry.ts +50 -15
  303. package/src/protocol/request/chat.ts +46 -19
  304. package/src/protocol/request/codex.ts +1 -1
  305. package/src/protocol/request/messages.ts +46 -14
  306. package/src/protocol/request/responses.ts +19 -10
  307. package/src/protocol/response/chat.ts +27 -12
  308. package/src/protocol/response/codex.ts +2 -2
  309. package/src/protocol/response/gemini.ts +1 -1
  310. package/src/protocol/response/messages.ts +6 -6
  311. package/src/protocol/response/omp-tool-call-markup.ts +1 -1
  312. package/src/protocol/response/responses.ts +3 -3
  313. package/src/protocol/sse-decoder.ts +64 -4
  314. package/src/protocol/stop-reason.ts +2 -2
  315. package/src/protocol/think-split.ts +347 -0
  316. package/src/protocol/translation/attachment-integrity.ts +3 -3
  317. package/src/protocol/translation/cache-controls.ts +8 -20
  318. package/src/protocol/translation/capabilities.ts +8 -8
  319. package/src/protocol/translation/dialect/deepseek.ts +10 -0
  320. package/src/protocol/translation/dialect/inband.ts +39 -0
  321. package/src/protocol/translation/dialect/model-thinking.ts +89 -0
  322. package/src/protocol/translation/dialect/qwen.ts +114 -0
  323. package/src/protocol/translation/dialect/resolver.ts +83 -0
  324. package/src/protocol/translation/dialect/rules.ts +93 -0
  325. package/src/protocol/translation/dialect/thinking-ladder.ts +245 -0
  326. package/src/protocol/translation/dialect/types.ts +58 -0
  327. package/src/protocol/translation/quirks.ts +18 -4
  328. package/src/protocol/translation/thinking.ts +12 -252
  329. package/src/protocol/translation/tool-id.ts +3 -3
  330. package/src/protocol/translation/tool-repair.ts +1 -1
  331. package/src/protocol/upstream/post-json.ts +6 -1
  332. package/src/protocol/upstream/send-claude-messages.ts +3 -3
  333. package/src/providers/adapters/base-adapter.ts +1 -1
  334. package/src/providers/adapters/compatible-adapter.ts +44 -5
  335. package/src/providers/authentication/jwt-validator.ts +0 -5
  336. package/src/providers/authentication/oauth-client.ts +254 -253
  337. package/src/providers/authentication/oauth-flow-store.ts +42 -7
  338. package/src/providers/authentication/oauth-refresh-service.ts +2 -2
  339. package/src/providers/bundled-discovery-providers.ts +6 -30
  340. package/src/providers/configured-openai-providers.ts +82 -97
  341. package/src/providers/configured-provider.ts +1 -1
  342. package/src/providers/default-registry.ts +23 -35
  343. package/src/providers/discovery/catalog/catalog-format.ts +13 -2
  344. package/src/providers/discovery/discovery-types.ts +0 -1
  345. package/src/providers/discovery/openai-model-discovery.ts +2 -2
  346. package/src/providers/discovery/probe-phases.ts +50 -15
  347. package/src/providers/discovery/probing-service.ts +18 -20
  348. package/src/providers/integrations/agentrouter/agentrouter.ts +14 -5
  349. package/src/providers/integrations/alibabacp/alibabacp.ts +2 -2
  350. package/src/providers/integrations/anthropic.ts +4 -11
  351. package/src/providers/integrations/antigravity/antigravity-oauth.ts +0 -1
  352. package/src/providers/integrations/antigravity/antigravity-protocol.ts +0 -14
  353. package/src/providers/integrations/antigravity/antigravity.ts +15 -26
  354. package/src/providers/integrations/buddy/buddy-catalog-shared.ts +2 -2
  355. package/src/providers/integrations/buddy/buddy-chat-shared.ts +20 -4
  356. package/src/providers/integrations/buddy/buddy-cli-headers.ts +19 -52
  357. package/src/providers/integrations/buddy/buddy-discovery-shared.ts +37 -6
  358. package/src/providers/integrations/buddy/buddy-identity.ts +108 -1
  359. package/src/providers/integrations/buddy/buddy-media.ts +16 -16
  360. package/src/providers/integrations/buddy/buddy-oauth-shared.ts +1 -1
  361. package/src/providers/integrations/buddy/codebuddy/cn-provider.ts +1 -2
  362. package/src/providers/integrations/buddy/codebuddy/provider.ts +5 -16
  363. package/src/providers/integrations/buddy/codebuddy/shared.ts +30 -9
  364. package/src/providers/integrations/buddy/workbuddy/provider.ts +6 -10
  365. package/src/providers/integrations/buddy/workbuddy/shared.ts +21 -3
  366. package/src/providers/integrations/claude/claude-betas.ts +1 -1
  367. package/src/providers/integrations/claude/claude-cch.ts +148 -148
  368. package/src/providers/integrations/claude/claude-credentials.ts +1 -1
  369. package/src/providers/integrations/claude/claude-slow-lane-retry.ts +1 -1
  370. package/src/providers/integrations/claude/claude.ts +3 -10
  371. package/src/providers/integrations/claude/fallback-credit.ts +3 -7
  372. package/src/providers/integrations/cline/cline.ts +2 -2
  373. package/src/providers/integrations/cloudflareai/cloudflareai.ts +4 -7
  374. package/src/providers/integrations/codex/codex-device-code.ts +101 -101
  375. package/src/providers/integrations/codex/codex-errors.ts +1 -1
  376. package/src/providers/integrations/codex/codex-oauth.ts +0 -1
  377. package/src/providers/integrations/codex/codex.ts +14 -15
  378. package/src/providers/integrations/commandcode/commandcode.ts +76 -116
  379. package/src/providers/integrations/commandcode/quota.ts +132 -48
  380. package/src/providers/integrations/cursor/cursor-http2.ts +11 -14
  381. package/src/providers/integrations/cursor/cursor-quota.ts +2 -6
  382. package/src/providers/integrations/cursor/cursor-request.ts +0 -87
  383. package/src/providers/integrations/cursor/cursor-shared.ts +0 -4
  384. package/src/providers/integrations/cursor/cursor.ts +53 -20
  385. package/src/providers/integrations/cursor/protocol/framing.ts +0 -56
  386. package/src/providers/integrations/cursor/protocol/messages.ts +0 -96
  387. package/src/providers/integrations/factory/factory.ts +13 -13
  388. package/src/providers/integrations/gemini.ts +11 -11
  389. package/src/providers/integrations/github/github-discovery.ts +55 -10
  390. package/src/providers/integrations/github/github-oauth.ts +6 -5
  391. package/src/providers/integrations/github/github.ts +47 -20
  392. package/src/providers/integrations/grok/grok-oauth.ts +261 -261
  393. package/src/providers/integrations/grok/grok-quota-frame.ts +1 -1
  394. package/src/providers/integrations/grok/grok-quota.ts +16 -7
  395. package/src/providers/integrations/grok/grok-ratelimit-store.ts +0 -5
  396. package/src/providers/integrations/grok/grok-ratelimit.ts +3 -0
  397. package/src/providers/integrations/grok/grok.ts +88 -42
  398. package/src/providers/integrations/inferhub.ts +4 -11
  399. package/src/providers/integrations/kilo/kilo-oauth.ts +1 -1
  400. package/src/providers/integrations/kimi/kimi.ts +4 -11
  401. package/src/providers/integrations/kiro/kiro-cache.ts +31 -0
  402. package/src/providers/integrations/kiro/kiro-catalog.ts +31 -5
  403. package/src/providers/integrations/kiro/kiro-discovery.ts +47 -43
  404. package/src/providers/integrations/kiro/kiro-machine-id.ts +6 -3
  405. package/src/providers/integrations/kiro/kiro-oauth.ts +523 -255
  406. package/src/providers/integrations/kiro/kiro-quota.ts +35 -47
  407. package/src/providers/integrations/kiro/kiro-request.ts +8 -11
  408. package/src/providers/integrations/kiro/kiro-stream.ts +13 -0
  409. package/src/providers/integrations/kiro/kiro.ts +91 -78
  410. package/src/providers/integrations/muse/muse-oauth.ts +0 -1
  411. package/src/providers/integrations/muse/muse-quota.ts +0 -4
  412. package/src/providers/integrations/muse/muse.ts +6 -9
  413. package/src/providers/integrations/opencode/fingerprint.ts +7 -5
  414. package/src/providers/integrations/opencode/provider.ts +17 -24
  415. package/src/providers/integrations/perplexity.ts +2 -2
  416. package/src/providers/integrations/qoder/qoder-catalog.ts +132 -17
  417. package/src/providers/integrations/qoder/qoder-checkin.ts +26 -8
  418. package/src/providers/integrations/qoder/qoder-native-identity-store.ts +9 -8
  419. package/src/providers/integrations/qoder/qoder-oauth.ts +165 -25
  420. package/src/providers/integrations/qoder/qoder-quota.ts +48 -6
  421. package/src/providers/integrations/qoder/qoder-realm.ts +22 -28
  422. package/src/providers/integrations/qoder/qoder-sse.ts +159 -0
  423. package/src/providers/integrations/qoder/qoder-umid.ts +33 -38
  424. package/src/providers/integrations/qoder/qoder-virtual.ts +14 -2
  425. package/src/providers/integrations/qoder/qoder.ts +521 -63
  426. package/src/providers/integrations/shared/connect.ts +0 -54
  427. package/src/providers/integrations/shared/hash.ts +15 -0
  428. package/src/providers/integrations/shared/session-cache.ts +136 -0
  429. package/src/providers/integrations/tokenharbor.ts +2 -2
  430. package/src/providers/integrations/trae/trae-cache.ts +7 -87
  431. package/src/providers/integrations/trae/trae-catalog.ts +1 -6
  432. package/src/providers/integrations/trae/trae-protocol.ts +17 -8
  433. package/src/providers/integrations/trae/trae-realm.ts +0 -7
  434. package/src/providers/integrations/trae/trae.ts +59 -19
  435. package/src/providers/integrations/xai/xai-oauth.ts +5 -5
  436. package/src/providers/integrations/zcode/oauth.ts +1 -1
  437. package/src/providers/integrations/zcode-start-plan/gifts.ts +265 -0
  438. package/src/providers/integrations/zcode-start-plan/oauth.ts +154 -0
  439. package/src/providers/integrations/zcode-start-plan/provider.ts +131 -0
  440. package/src/providers/integrations/zcode-start-plan/quota.ts +152 -0
  441. package/src/providers/integrations/zcode-start-plan/zcode_prompt.json +10 -0
  442. package/src/providers/operations/account-error-classification.ts +131 -116
  443. package/src/providers/operations/account-health-service.ts +31 -39
  444. package/src/providers/operations/account-reset-service.ts +58 -1
  445. package/src/providers/operations/cli-platform.ts +31 -5
  446. package/src/providers/operations/client-version-resolver.ts +4 -6
  447. package/src/providers/operations/client-versions.ts +10 -7
  448. package/src/providers/operations/provider-catalog-seeder.ts +2 -0
  449. package/src/providers/operations/provider-catalog-service.ts +22 -6
  450. package/src/providers/operations/provider-credential-service.ts +35 -6
  451. package/src/providers/operations/upstream-deadline.ts +3 -3
  452. package/src/providers/provider-contracts.ts +65 -10
  453. package/src/providers/provider-credential-resolver.ts +1 -1
  454. package/src/providers/provider-metadata.ts +17 -2
  455. package/src/providers/provider-model-definition.ts +22 -1
  456. package/src/providers/provider-probe.ts +4 -2
  457. package/src/providers/provider-registry.ts +4 -4
  458. package/src/providers/provider-usage.ts +81 -16
  459. package/src/providers/quota/cache.ts +29 -0
  460. package/src/providers/quota/quota-contracts.ts +28 -10
  461. package/src/providers/quota/refresh.ts +29 -6
  462. package/src/providers/quota/snapshot.ts +94 -0
  463. package/src/providers/search/search-provider.ts +6 -8
  464. package/src/providers/search/search-providers.ts +2 -1
  465. package/src/security/abuse.ts +8 -8
  466. package/src/security/admission/reasons.ts +17 -17
  467. package/src/security/admission/service.ts +2 -2
  468. package/src/security/admission/ttl.ts +61 -61
  469. package/src/security/api-key-auth.ts +20 -8
  470. package/src/security/client-router-fingerprint.ts +4 -4
  471. package/src/security/credential-codec.ts +3 -3
  472. package/src/security/csrf.ts +2 -2
  473. package/src/security/ip-boundary.ts +16 -189
  474. package/src/security/model-access-rule.ts +1 -1
  475. package/src/security/outbound-headers.ts +18 -0
  476. package/src/shared/coerce.ts +19 -0
  477. package/src/shared/invoke-guard.ts +47 -0
  478. package/src/transport/contracts.ts +13 -4
  479. package/src/transport/dispatch/attempt-finalize.ts +5 -4
  480. package/src/transport/dispatch/attempt-loop.ts +10 -7
  481. package/src/transport/dispatch/capture-policy.ts +34 -37
  482. package/src/transport/dispatch/empty-stream.ts +28 -25
  483. package/src/transport/dispatch/generation.ts +33 -53
  484. package/src/transport/dispatch/leases.ts +8 -8
  485. package/src/transport/dispatch/native-shared.ts +127 -0
  486. package/src/transport/dispatch/proxy-request.ts +10 -8
  487. package/src/transport/dispatch/responses-compact.ts +35 -56
  488. package/src/transport/dispatch/retry-policy.ts +4 -67
  489. package/src/transport/dispatch/stream-bridge.ts +1 -1
  490. package/src/transport/dispatch/streaming/encoder.ts +18 -72
  491. package/src/transport/dispatch/streaming/finalize.ts +11 -10
  492. package/src/transport/dispatch/streaming/stream.ts +7 -3
  493. package/src/transport/dispatch/streaming/thinking-loop.ts +1 -1
  494. package/src/transport/dispatch/systemone.ts +27 -55
  495. package/src/transport/dispatch/token-count.ts +49 -46
  496. package/src/transport/dispatch/websearch.ts +24 -50
  497. package/src/transport/drain-endpoint.ts +3 -3
  498. package/src/transport/failure-policy.ts +19 -6
  499. package/src/transport/guards/auth-guard.ts +6 -11
  500. package/src/transport/guards/body-policy.ts +7 -7
  501. package/src/transport/guards/error-lifecycle.ts +18 -3
  502. package/src/transport/guards/guard-errors.ts +7 -3
  503. package/src/transport/guards/headroom-guard.ts +2 -2
  504. package/src/transport/guards/pipeline.ts +7 -7
  505. package/src/transport/guards/readiness-guard.ts +16 -2
  506. package/src/transport/guards/request-context.ts +15 -20
  507. package/src/transport/request/preparer.ts +306 -27
  508. package/src/transport/request/rtk/compress-request.ts +3 -3
  509. package/src/transport/request/rtk/rtk-constants.ts +2 -2
  510. package/src/transport/request/rtk/rtk-filters.ts +8 -8
  511. package/src/transport/request/state.ts +5 -20
  512. package/src/transport/request/system-prompt-injection.ts +74 -11
  513. package/src/transport/request/tenant-preferences.ts +22 -0
  514. package/src/transport/routing/candidate-index.ts +2 -2
  515. package/src/transport/routing/round-robin.ts +1 -1
  516. package/src/transport/routing/route-catalog.ts +113 -19
  517. package/src/transport/routing/route-model.ts +131 -38
  518. package/src/transport/routing/router.ts +154 -39
  519. package/src/transport/shutdown-notice.ts +21 -9
  520. package/src/transport/surface/adapters.ts +3 -3
  521. package/src/transport/surface/chat/encode.ts +7 -7
  522. package/src/transport/surface/chat/parse.ts +7 -7
  523. package/src/transport/surface/completion.ts +3 -3
  524. package/src/transport/surface/content-parts.ts +1 -1
  525. package/src/transport/surface/dialects.ts +2 -2
  526. package/src/transport/surface/messages/encode.ts +1 -1
  527. package/src/transport/surface/messages/parse.ts +3 -3
  528. package/src/transport/surface/responses/adapter.ts +1 -1
  529. package/src/transport/surface/responses/errors.ts +0 -9
  530. package/src/transport/vision-handoff-cache.ts +58 -0
  531. package/src/transport/vision-handoff.ts +376 -0
  532. package/src/workers/account-identity-sweep.ts +2 -2
  533. package/src/workers/buddy-growth-tasks.ts +392 -346
  534. package/src/workers/daily-checkin.ts +102 -49
  535. package/src/workers/maintenance-lease.ts +2 -35
  536. package/src/workers/oauth-refresh-sweep.ts +8 -1
  537. package/src/workers/quota-refresh-sweep.ts +120 -82
  538. package/src/workers/scheduler.ts +67 -4
  539. package/src/workers/sweep-runner.ts +24 -5
  540. package/src/workers/zcode-gift-claim.ts +108 -0
  541. package/dist/dashboard/assets/AccountCooldown-Bjo92Qat.js +0 -1
  542. package/dist/dashboard/assets/AccountCooldown-Bjo92Qat.js.gz +0 -0
  543. package/dist/dashboard/assets/BannedPage-2TJD37Hb.js.gz +0 -0
  544. package/dist/dashboard/assets/CliToolDetailPage-xefJNRmJ.js.gz +0 -0
  545. package/dist/dashboard/assets/CliToolsPage-Rj9iHQ3F.js.gz +0 -0
  546. package/dist/dashboard/assets/CombosPage-JEbYJFxw.js +0 -1
  547. package/dist/dashboard/assets/CombosPage-JEbYJFxw.js.gz +0 -0
  548. package/dist/dashboard/assets/ConsoleLogPage-C3xx_inM.js +0 -1
  549. package/dist/dashboard/assets/ConsoleLogPage-C3xx_inM.js.gz +0 -0
  550. package/dist/dashboard/assets/CustomizationPage-Dw306gJU.js +0 -1
  551. package/dist/dashboard/assets/CustomizationPage-Dw306gJU.js.gz +0 -0
  552. package/dist/dashboard/assets/HealthEventsModal-jaBo1TWV.js +0 -1
  553. package/dist/dashboard/assets/HealthEventsModal-jaBo1TWV.js.gz +0 -0
  554. package/dist/dashboard/assets/LoginPage-C4w7vea-.js.gz +0 -0
  555. package/dist/dashboard/assets/ModelPicker-BtPcAfot.js.gz +0 -0
  556. package/dist/dashboard/assets/OverviewPage-CGC5gNs1.js +0 -1
  557. package/dist/dashboard/assets/OverviewPage-CGC5gNs1.js.gz +0 -0
  558. package/dist/dashboard/assets/ProviderDetailPage-DQcthNGt.js +0 -4
  559. package/dist/dashboard/assets/ProviderDetailPage-DQcthNGt.js.gz +0 -0
  560. package/dist/dashboard/assets/ProviderIcon-FB6yS4v1.js +0 -1
  561. package/dist/dashboard/assets/ProviderIcon-FB6yS4v1.js.gz +0 -0
  562. package/dist/dashboard/assets/ProvidersPage-0OsXRGXe.js +0 -1
  563. package/dist/dashboard/assets/ProvidersPage-0OsXRGXe.js.gz +0 -0
  564. package/dist/dashboard/assets/ProxyPage-DdPc8slb.js +0 -9
  565. package/dist/dashboard/assets/ProxyPage-DdPc8slb.js.gz +0 -0
  566. package/dist/dashboard/assets/QuotaPage-Ci5O4BnH.js +0 -1
  567. package/dist/dashboard/assets/QuotaPage-Ci5O4BnH.js.gz +0 -0
  568. package/dist/dashboard/assets/SettingsPage-CDZjTt87.js +0 -1
  569. package/dist/dashboard/assets/SettingsPage-CDZjTt87.js.gz +0 -0
  570. package/dist/dashboard/assets/SetupPage-BQS0Rx_u.js.gz +0 -0
  571. package/dist/dashboard/assets/SortableList-BAL5npBC.js +0 -1
  572. package/dist/dashboard/assets/SortableList-BAL5npBC.js.gz +0 -0
  573. package/dist/dashboard/assets/StudioPage-TP7voAQ0.js.gz +0 -0
  574. package/dist/dashboard/assets/UsagePage-Cj_fEzhg.js +0 -56
  575. package/dist/dashboard/assets/UsagePage-Cj_fEzhg.js.gz +0 -0
  576. package/dist/dashboard/assets/api-keys-MbI7e7rt.js +0 -1
  577. package/dist/dashboard/assets/api-keys-MbI7e7rt.js.gz +0 -0
  578. package/dist/dashboard/assets/app-B2vyPoII.css +0 -2
  579. package/dist/dashboard/assets/app-B2vyPoII.css.gz +0 -0
  580. package/dist/dashboard/assets/assert-C0YYuZJ4.js.gz +0 -0
  581. package/dist/dashboard/assets/badge-B_DMpMJN.js.gz +0 -0
  582. package/dist/dashboard/assets/banner-DMrMPmth.js.gz +0 -0
  583. package/dist/dashboard/assets/card-Be5Uzamx.js.gz +0 -0
  584. package/dist/dashboard/assets/cli-tools-muFRphDu.js.gz +0 -0
  585. package/dist/dashboard/assets/clipboard-button-DOfrVmsi.js.gz +0 -0
  586. package/dist/dashboard/assets/dialog-C9omdjQt.js.gz +0 -0
  587. package/dist/dashboard/assets/drawer-Bm8WKaGY.js.gz +0 -0
  588. package/dist/dashboard/assets/entry-DS90gUks.js +0 -1
  589. package/dist/dashboard/assets/entry-DS90gUks.js.gz +0 -0
  590. package/dist/dashboard/assets/entry-j40LJotO.js +0 -4
  591. package/dist/dashboard/assets/entry-j40LJotO.js.gz +0 -0
  592. package/dist/dashboard/assets/entry-q5HwZ8nN.js.gz +0 -0
  593. package/dist/dashboard/assets/formatters-BTScrQpe.js.gz +0 -0
  594. package/dist/dashboard/assets/github-badge-Orjuzemr.js.gz +0 -0
  595. package/dist/dashboard/assets/index-cPrX5vn6.js.gz +0 -0
  596. package/dist/dashboard/assets/inline-Dvux8w-0.js.gz +0 -0
  597. package/dist/dashboard/assets/input-CU1Rgf3p.js.gz +0 -0
  598. package/dist/dashboard/assets/list-row-CzFvogbj.js.gz +0 -0
  599. package/dist/dashboard/assets/live-KaG15HLQ.js +0 -1
  600. package/dist/dashboard/assets/live-KaG15HLQ.js.gz +0 -0
  601. package/dist/dashboard/assets/minimize-2-D-MN8W8a.js +0 -1
  602. package/dist/dashboard/assets/minimize-2-D-MN8W8a.js.gz +0 -0
  603. package/dist/dashboard/assets/mutation-JJGQm8TD.js.gz +0 -0
  604. package/dist/dashboard/assets/numbers-BNDZnsUE.js +0 -2
  605. package/dist/dashboard/assets/numbers-BNDZnsUE.js.gz +0 -0
  606. package/dist/dashboard/assets/query-keys-CLaNz8Xz.js.gz +0 -0
  607. package/dist/dashboard/assets/quota-DZGyp0N5.js +0 -1
  608. package/dist/dashboard/assets/quota-DZGyp0N5.js.gz +0 -0
  609. package/dist/dashboard/assets/routing-BEH32QOC.js.gz +0 -0
  610. package/dist/dashboard/assets/select-B3iTetHN.js.gz +0 -0
  611. package/dist/dashboard/assets/settings-Dt3Eqbnq.js +0 -1
  612. package/dist/dashboard/assets/settings-Dt3Eqbnq.js.gz +0 -0
  613. package/dist/dashboard/assets/stack-KrOrm7JZ.js.gz +0 -0
  614. package/dist/dashboard/assets/state-DLS7IDMy.js.gz +0 -0
  615. package/dist/dashboard/assets/stream-retry-5QnVuSSo.js +0 -1
  616. package/dist/dashboard/assets/stream-retry-5QnVuSSo.js.gz +0 -0
  617. package/dist/dashboard/assets/switch-42ItvbAs.js.gz +0 -0
  618. package/dist/dashboard/assets/useMutation-Dmnfh1Ue.js.gz +0 -0
  619. package/src/network/response-headers.ts +0 -39
  620. package/src/protocol/response/dsml.ts +0 -38
  621. package/src/providers/integrations/xiaomi-mimo/think-stream.ts +0 -229
  622. /package/dist/dashboard/assets/{app-wvwCi4Eh.js → app-Bgd3zANQ.js} +0 -0
  623. /package/dist/dashboard/assets/{app-wvwCi4Eh.js.gz → app-Bgd3zANQ.js.gz} +0 -0
  624. /package/dist/dashboard/assets/brand/{og_bansos.webp → og_bansos_initial.webp} +0 -0
  625. /package/dist/dashboard/assets/brand/{og_image.webp → og_image_initial.webp} +0 -0
@@ -2,6 +2,7 @@
2
2
  * Canonical → Claude Messages request builder.
3
3
  */
4
4
  import { allParts, type CanonicalRequest, type ContentPart } from "../canonical-model";
5
+ import type { DialectPolicy } from "../translation/dialect/types";
5
6
  import { buildAnthropicThinkingPayload } from "../../protocol/reasoning";
6
7
  import { anthropicCacheControl } from "../../protocol/translation/cache-controls";
7
8
  import {
@@ -28,14 +29,17 @@ import {
28
29
  } from "../codecs";
29
30
  import { log } from "../../observability/logger";
30
31
  import { base64DecodedByteLength, clampImage } from "../image-clamp";
31
- import { clampReasoningEffort, claudeUsesAdaptiveThinking, LEVEL_TO_BUDGET, resolveSupportedReasoningEfforts } from "../../protocol/translation/thinking";
32
+ import { LEVEL_TO_BUDGET } from "../../protocol/translation/thinking";
33
+ import type { ThinkingConfig } from "../../protocol/translation/dialect/thinking-ladder";
34
+ import { clampThinkingLevelForModel } from "../../protocol/translation/dialect/model-thinking";
35
+ import { resolveThinkingForModel } from "../../protocol/translation/dialect/thinking-ladder";
32
36
  import { claudeResponseToEvents, parseClaudeSseStream } from "../response/messages";
33
37
 
34
38
  /**
35
39
  * Gateway default when the caller sent no max_tokens (Anthropic requires
36
40
  * the field). Explicit and warned at the call site — never silent.
37
41
  */
38
- export const ANTHROPIC_DEFAULT_MAX_TOKENS = WIRE_TOKEN_BOUNDS["messages"]?.defaultMaxTokens ?? 4_096;
42
+ const ANTHROPIC_DEFAULT_MAX_TOKENS = WIRE_TOKEN_BOUNDS["messages"]?.defaultMaxTokens ?? 4_096;
39
43
 
40
44
  /** Anthropic Messages' documented base64-image media-type allowlist. */
41
45
  const ANTHROPIC_IMAGE_MEDIA_TYPES = new Set([
@@ -556,7 +560,13 @@ export function claudeFallbacksChain(
556
560
 
557
561
  export function canonicalToClaudeMessagesPayload(
558
562
  request: CanonicalRequest,
559
- options: { isOAuth?: boolean; maxBytesOverride?: number } = {},
563
+ options: {
564
+ isOAuth?: boolean;
565
+ maxBytesOverride?: number;
566
+ dialectPolicy?: DialectPolicy;
567
+ thinking?: ThinkingConfig;
568
+ maxOutputTokens?: number;
569
+ } = {},
560
570
  ): ClaudeWireObject {
561
571
  const seen = new Map<string, number>();
562
572
  const idMap = new Map<string, string>();
@@ -653,7 +663,11 @@ export function canonicalToClaudeMessagesPayload(
653
663
  const normalizedControls = normalizeWireMaxTokens(request.generation_controls);
654
664
  const requestedMax = normalizedControls.max_tokens;
655
665
  const hasTools = (request.tools?.length ?? 0) > 0;
656
- let maxTokens = resolveWireMaxTokens("messages", { max_tokens: requestedMax }, { hasTools, isOAuth }) ?? ANTHROPIC_DEFAULT_MAX_TOKENS;
666
+ let maxTokens = resolveWireMaxTokens("messages", { max_tokens: requestedMax }, {
667
+ hasTools,
668
+ isOAuth,
669
+ ...(options.maxOutputTokens === undefined ? {} : { maxOutputTokens: options.maxOutputTokens }),
670
+ }) ?? ANTHROPIC_DEFAULT_MAX_TOKENS;
657
671
  if (requestedMax === undefined) {
658
672
  log.warn("claude: max_tokens absent, defaulting", {
659
673
  max_tokens: maxTokens,
@@ -665,21 +679,32 @@ export function canonicalToClaudeMessagesPayload(
665
679
  });
666
680
  }
667
681
  const thinkingEnabled = isThinkingEnabled(request);
682
+ // Real ceilings (P2-12): an impossible thinking budget surfaces as a typed
683
+ // local 400 instead of an over-ceiling max_tokens that 400s opaquely upstream.
684
+ // Non-OAuth caps at the model's catalog output limit (when known) so the
685
+ // thinking guard can fire instead of shipping an over-catalog-limit
686
+ // `max_tokens`; OAuth keeps its own ceiling per the table.
687
+ const maxAllowed = isOAuth
688
+ ? WIRE_TOKEN_BOUNDS["messages"]?.oauthCeiling ?? Number.MAX_SAFE_INTEGER
689
+ : options.maxOutputTokens ?? Number.MAX_SAFE_INTEGER;
668
690
  if (thinkingEnabled && request.reasoning?.budget_tokens !== undefined) {
669
691
  maxTokens = ensureMaxTokensForThinking(
670
692
  maxTokens,
671
693
  request.reasoning.budget_tokens,
672
- Number.MAX_SAFE_INTEGER,
694
+ maxAllowed,
673
695
  );
674
696
  }
675
697
  // Whether this model speaks adaptive thinking (`type: "adaptive"` +
676
698
  // `output_config.effort`) or budget thinking (`type: "enabled"` +
677
699
  // `budget_tokens`). The two generations reject each other's shape, so the
678
- // choice is made from the model id, not from the caller's canonical intent —
679
- // a chat/responses caller states only an effort, and a budget-era model
680
- // answers the adaptive shape with "adaptive thinking is not supported on this
681
- // model" (verified live: opus-4-5/sonnet-4-5/haiku-4-5).
682
- const adaptiveThinking = claudeUsesAdaptiveThinking(request.model);
700
+ // choice is made from the resolved thinking config, not from the caller's
701
+ // canonical intent — a chat/responses caller states only an effort, and a
702
+ // budget-era model answers the adaptive shape with "adaptive thinking is not
703
+ // supported on this model" (verified live: opus-4-5/sonnet-4-5/haiku-4-5).
704
+ // When no config is supplied (direct-route callers), fall back to resolving
705
+ // it from the model/wire — the resolved `mode` IS the generation answer.
706
+ const resolvedThinking = options.thinking ?? resolveThinkingForModel(request.model, "", "messages");
707
+ const adaptiveThinking = resolvedThinking.mode === "anthropic-adaptive";
683
708
  let effectiveBudgetTokens = request.reasoning?.budget_tokens;
684
709
  // Derive a budget from the level whenever a budget-era model is asked to
685
710
  // reason by effort alone. The gate is NOT `isThinkingEnabled`: a chat/responses
@@ -699,7 +724,13 @@ export function canonicalToClaudeMessagesPayload(
699
724
  if (!adaptiveThinking && effectiveBudgetTokens !== undefined) {
700
725
  // The upstream requires the budget to stay below `max_tokens`; raise
701
726
  // max_tokens to leave room for the answer rather than let it 400.
702
- maxTokens = ensureMaxTokensForThinking(maxTokens, effectiveBudgetTokens, Number.MAX_SAFE_INTEGER);
727
+ maxTokens = ensureMaxTokensForThinking(maxTokens, effectiveBudgetTokens, maxAllowed);
728
+ }
729
+ // Final clamp to the model's catalog output limit: thinking may have raised
730
+ // `max_tokens` (e.g. under an OAuth ceiling wider than the catalog limit), so
731
+ // the value that ships must never exceed what the model can actually emit.
732
+ if (options.maxOutputTokens !== undefined && maxTokens > options.maxOutputTokens) {
733
+ maxTokens = options.maxOutputTokens;
703
734
  }
704
735
  const payload: ClaudeWireObject = {
705
736
  model: request.model,
@@ -748,7 +779,8 @@ export function canonicalToClaudeMessagesPayload(
748
779
  }
749
780
  const parallelToolCallsDisabled =
750
781
  request.generation_controls.parallel_tool_calls === false;
751
- if (rawToolChoice !== undefined || parallelToolCallsDisabled) {
782
+ const supportsToolChoice = options.dialectPolicy?.supportsToolChoice ?? true;
783
+ if (supportsToolChoice && (rawToolChoice !== undefined || parallelToolCallsDisabled)) {
752
784
  const baseChoice = rawToolChoice as Record<string, unknown> | undefined;
753
785
  const choice: Record<string, unknown> = baseChoice
754
786
  ? { ...baseChoice }
@@ -835,9 +867,9 @@ export function canonicalToClaudeMessagesPayload(
835
867
  // is NOT a wire field — display rides on `thinking.display` above.
836
868
  // Clamp to the model's published ladder exactly like the chat/Responses
837
869
  // codecs: an out-of-ladder tier is an upstream 4xx.
838
- const effort = clampReasoningEffort(
870
+ const effort = clampThinkingLevelForModel(
871
+ resolvedThinking,
839
872
  request.reasoning.effort,
840
- resolveSupportedReasoningEfforts(request.model, "messages"),
841
873
  );
842
874
  const taskBudget = request.reasoning.task_budget;
843
875
  // `output_config.effort` rides every generation: budget-era models accept it
@@ -7,10 +7,10 @@ import { markLatestBreakpoint } from "../../protocol/translation/cache-controls"
7
7
  import { pickWireSupportedControls } from "../../protocol/translation/capabilities";
8
8
  import { normalizeGenerationControls } from "../../protocol/translation/normalize";
9
9
  import { resolveWireMaxTokens } from "../../protocol/translation/quirks";
10
- import {
11
- clampReasoningEffort,
12
- resolveSupportedReasoningEfforts,
13
- } from "../../protocol/translation/thinking";
10
+ import type { DialectPolicy } from "../../protocol/translation/dialect/types";
11
+ import type { ThinkingConfig } from "../../protocol/translation/dialect/thinking-ladder";
12
+ import { resolveThinkingForModel } from "../../protocol/translation/dialect/thinking-ladder";
13
+ import { clampThinkingLevelForModel } from "../../protocol/translation/dialect/model-thinking";
14
14
  import { isClaudeBillingHeaderText, resolveImageSource, resolveOutputVerbosity } from "../codecs";
15
15
  import { resolvePromptCacheKey } from "../../providers/operations/session-resolution";
16
16
  import { reasoningEffortFromIntent } from "../../protocol/reasoning";
@@ -27,7 +27,7 @@ const RESPONSES_CACHEABLE_BLOCK_TYPES: ReadonlySet<string> = new Set([
27
27
  ]);
28
28
 
29
29
  /** Marks the latest stable input block with an explicit prompt-cache breakpoint. */
30
- export function markLatestResponsesCacheBreakpoint(input: Array<Record<string, unknown>>): void {
30
+ function markLatestResponsesCacheBreakpoint(input: Array<Record<string, unknown>>): void {
31
31
  markLatestBreakpoint(input, {
32
32
  isInputItem: (item) =>
33
33
  item["type"] === "message" && (item["role"] === "user" || item["role"] === "developer"),
@@ -63,9 +63,13 @@ export function canonicalToResponsesPayload(
63
63
  options: {
64
64
  readonly supportsPromptCaching?: boolean;
65
65
  readonly promptCacheKey?: string;
66
+ readonly dialectPolicy?: DialectPolicy;
67
+ readonly thinking?: ThinkingConfig;
68
+ readonly maxOutputTokens?: number;
66
69
  } = {},
67
70
  ): Record<string, unknown> {
68
71
  const supportsPromptCaching = options.supportsPromptCaching ?? true;
72
+ const resolvedThinking = options.thinking ?? resolveThinkingForModel(request.model, "", "responses");
69
73
  const input: Array<Record<string, unknown>> = [];
70
74
  // `pending_safety_checks` have no canonical content-part representation, so
71
75
  // `parseResponsesRequest` retains them per-item in the scoped item-metadata
@@ -317,10 +321,7 @@ export function canonicalToResponsesPayload(
317
321
  // this wire. Codex already derives its tier this way.
318
322
  const requestedEffort = reasoningEffortFromIntent(request.reasoning);
319
323
  if (requestedEffort !== undefined) {
320
- const effort = clampReasoningEffort(
321
- requestedEffort,
322
- resolveSupportedReasoningEfforts(request.model, "responses"),
323
- );
324
+ const effort = clampThinkingLevelForModel(resolvedThinking, requestedEffort);
324
325
  if (effort !== undefined) reasoning.effort = effort;
325
326
  }
326
327
  if (request.reasoning.mode !== undefined) reasoning.mode = request.reasoning.mode;
@@ -328,6 +329,12 @@ export function canonicalToResponsesPayload(
328
329
  if (request.reasoning.summary_mode !== undefined)
329
330
  reasoning.summary = request.reasoning.summary_mode;
330
331
  else if (request.reasoning.summary !== undefined) reasoning.summary = request.reasoning.summary;
332
+ else if (requestedEffort !== undefined) reasoning.summary = "detailed";
333
+ // When the caller asked for reasoning effort but expressed no summary
334
+ // preference, request a detailed summary. Upstreams (GitHub Copilot's
335
+ // gpt-6-luna etc.) only stream `reasoning_summary_text` when the request
336
+ // asks for it — without this the reasoning is present on the wire yet
337
+ // never reaches the client.
331
338
  if (Object.keys(reasoning).length > 0) payload.reasoning = reasoning;
332
339
  }
333
340
  const picked = pickWireSupportedControls(normalizeGenerationControls(request.generation_controls), "responses");
@@ -338,7 +345,9 @@ export function canonicalToResponsesPayload(
338
345
  // Resolved unconditionally so the wire's `defaultMaxTokens` applies: an
339
346
  // omitted limit must reach the upstream as a real number, not as an absent
340
347
  // field (see the worked "wkwk" case in the WIRE_TOKEN_BOUNDS comment).
341
- const bounded = resolveWireMaxTokens("responses", { max_tokens: requested });
348
+ const bounded = resolveWireMaxTokens("responses", { max_tokens: requested }, {
349
+ ...(options.maxOutputTokens === undefined ? {} : { maxOutputTokens: options.maxOutputTokens }),
350
+ });
342
351
  if (bounded !== undefined) picked["max_output_tokens"] = bounded;
343
352
  Object.assign(payload, picked);
344
353
  // Responses top-level params carried as `extension:responses.<field>` and
@@ -9,13 +9,15 @@ import { gatewayErrorFromStreamError } from "../stream-error-frames";
9
9
  import { mergeResponsesUsage } from "./responses";
10
10
  import { usageFromProvider, readReasoningText } from "../../providers/provider-usage";
11
11
  import { createToolEmitLedger } from "../tool-identity";
12
- import { MimoThinkSplitter, stripThinkingTags } from "../../providers/integrations/xiaomi-mimo/think-stream";
13
- import { DsmlToolCallParser, parseDsmlToolCalls } from "./dsml";
12
+ import { ThinkSplitter, stripThinkingTags } from "../think-split";
13
+ import { DsmlStreamParser, parseDsmlToolCalls } from "../dsml";
14
14
  import {
15
15
  OmpToolCallMarkupParser,
16
16
  parseOmpToolCallMarkup,
17
17
  type OmpTextToolCall,
18
18
  } from "./omp-tool-call-markup";
19
+ import type { DialectPolicy } from "../translation/dialect/types";
20
+ import { QwenToolCallScanner } from "../translation/dialect/qwen";
19
21
 
20
22
 
21
23
  export function mapChatStopReason(
@@ -83,11 +85,16 @@ export function parseChatResponseToEvents(
83
85
  const contentWithoutMarkup = recoveredMarkup?.text ?? content;
84
86
  const recovered =
85
87
  typeof contentWithoutMarkup === "string"
86
- ? parseDsmlToolCalls(contentWithoutMarkup, declaredToolNames)
88
+ ? parseDsmlToolCalls(contentWithoutMarkup, { allowedNames: declaredToolNames })
87
89
  : undefined;
88
- const visible = recovered === undefined ? contentWithoutMarkup : recovered.text;
90
+ const visible =
91
+ recovered === undefined
92
+ ? contentWithoutMarkup
93
+ : "text" in recovered
94
+ ? recovered.text
95
+ : recovered.clean;
89
96
  if (typeof visible === "string" && visible.includes("<think")) {
90
- const think = new MimoThinkSplitter();
97
+ const think = new ThinkSplitter();
91
98
  const split = think.push(visible);
92
99
  const rest = think.flush();
93
100
  const reasoning = `${split.reasoning}${rest.reasoning}`;
@@ -237,10 +244,12 @@ export async function* decodeChatSseStream(
237
244
  body: ReadableStream<Uint8Array>,
238
245
  request: CanonicalRequest,
239
246
  signal?: AbortSignal,
247
+ options?: { readonly dialectPolicy?: DialectPolicy },
240
248
  ): AsyncIterable<CanonicalEvent> {
241
249
  let seq = 1;
242
250
  let finishReason: unknown;
243
251
  let rawUsage: Record<string, unknown> | undefined;
252
+ const inbandDialect = options?.dialectPolicy?.inband;
244
253
  // Last seen tool-call id (and name) per wire index. OpenAI streams the id
245
254
  // (and name) only on the first fragment of each call; later fragments carry
246
255
  // just `function.arguments`. Without this memory every continuation fragment
@@ -257,11 +266,11 @@ export async function* decodeChatSseStream(
257
266
  const recoveredMarkupCalls: OmpTextToolCall[] = [];
258
267
  // Single emission ledger: one entry per logical call.
259
268
  const ledger = createToolEmitLedger();
260
- const think = new MimoThinkSplitter();
269
+ const think = new ThinkSplitter();
261
270
  // Separate splitter for the reasoning channel: `reasoning_content` is its own
262
271
  // stream, and interleaving it into the answer splitter would let a tag in one
263
272
  // channel swallow the other's text.
264
- const reasoningThink = new MimoThinkSplitter();
273
+ const reasoningThink = new ThinkSplitter();
265
274
  const declaredToolNames = declaredToolNamesForRequest(request);
266
275
  const callableToolNames = callableToolNamesForRequest(request);
267
276
  const contentMarkup =
@@ -269,7 +278,13 @@ export async function* decodeChatSseStream(
269
278
  const reasoningMarkup =
270
279
  (request.tools?.length ?? 0) > 0 ? new OmpToolCallMarkupParser(declaredToolNames) : undefined;
271
280
  const dsmlMarkup =
272
- (request.tools?.length ?? 0) > 0 ? new DsmlToolCallParser(declaredToolNames) : undefined;
281
+ (request.tools?.length ?? 0) > 0 && inbandDialect !== "qwen"
282
+ ? new DsmlStreamParser({ allowedNames: declaredToolNames })
283
+ : undefined;
284
+ const qwenMarkup =
285
+ (request.tools?.length ?? 0) > 0 && inbandDialect === "qwen"
286
+ ? new QwenToolCallScanner(declaredToolNames)
287
+ : undefined;
273
288
  yield {
274
289
  type: "response_start",
275
290
  sequence_number: seq++,
@@ -315,7 +330,7 @@ export async function* decodeChatSseStream(
315
330
  const raw = delta["content"] as string;
316
331
  const markup = contentMarkup?.push(raw);
317
332
  if (markup !== undefined) recoveredMarkupCalls.push(...markup.calls);
318
- const recovered = dsmlMarkup?.push(markup?.text ?? raw);
333
+ const recovered = (qwenMarkup ?? dsmlMarkup)?.push(markup?.text ?? raw);
319
334
  const visible = recovered?.text ?? markup?.text ?? raw;
320
335
  const split = think.push(visible);
321
336
  if (split.reasoning.length > 0) {
@@ -472,8 +487,8 @@ export async function* decodeChatSseStream(
472
487
  "platform_unavailable",
473
488
  502,
474
489
  error instanceof Error
475
- ? `Malformed SSE event: ${error.message}`
476
- : "Malformed SSE event",
490
+ ? `Malformed SSE event: ${error.message} (request {requestId}). The upstream sent something we can't read — retry 🌙`
491
+ : `Malformed SSE event (request {requestId}). The upstream sent something we can't read — retry 🌙`,
477
492
  {},
478
493
  "upstream",
479
494
  );
@@ -500,7 +515,7 @@ export async function* decodeChatSseStream(
500
515
  }
501
516
  }
502
517
  }
503
- const dsmlMarkupTail = dsmlMarkup?.flush();
518
+ const dsmlMarkupTail = (qwenMarkup ?? dsmlMarkup)?.flush();
504
519
  if (dsmlMarkupTail !== undefined) {
505
520
  if (dsmlMarkupTail.text.length > 0) {
506
521
  const split = think.push(dsmlMarkupTail.text);
@@ -15,7 +15,7 @@ import { usageFromProvider, readResponsesReasoningDelta } from '../../providers/
15
15
  import { canonicalTerminal, decodeCodexToolCallId, isRecord, readOutputIndex } from '../codecs';
16
16
  import { gatewayErrorFromStreamError } from '../stream-error-frames';
17
17
 
18
- export function mapCodexStopReason(
18
+ function mapCodexStopReason(
19
19
  status: unknown,
20
20
  hasToolCall: boolean,
21
21
  endTurn?: unknown,
@@ -364,7 +364,7 @@ export class CodexStreamFrameProcessor {
364
364
  throw new GatewayError(
365
365
  "tool_call_loop_detected",
366
366
  502,
367
- "Codex whitespace-loop guard tripped: tool call emitted only whitespace argument deltas past the safety threshold",
367
+ "Codex whitespace-loop guard tripped (request {requestId}): tool call emitted only whitespace argument deltas past the safety threshold. Caught in a loop — breaking the cycle 🔁",
368
368
  { call_key: key, event_count: next.count, char_count: next.chars },
369
369
  );
370
370
  }
@@ -124,7 +124,7 @@ export function decodeGeminiStreamEvent(
124
124
  throw new GatewayError(
125
125
  "platform_unavailable",
126
126
  502,
127
- error instanceof Error ? `Malformed SSE event: ${error.message}` : "Malformed SSE event",
127
+ error instanceof Error ? `Malformed SSE event: ${error.message} (request {requestId}). The upstream sent something we can't read — retry 🌙` : `Malformed SSE event (request {requestId}). The upstream sent something we can't read — retry 🌙`,
128
128
  {},
129
129
  "upstream",
130
130
  );
@@ -51,7 +51,7 @@ export function parseClaudeContent(value: unknown, isOAuth = false): ContentPart
51
51
  throw new GatewayError(
52
52
  "platform_unavailable",
53
53
  502,
54
- "Claude response tool_use is missing id or name",
54
+ "Claude response tool_use is missing id or name (request {requestId}). The upstream came back empty-handed — retry 🌙",
55
55
  {},
56
56
  "upstream",
57
57
  );
@@ -69,7 +69,7 @@ export function parseClaudeContent(value: unknown, isOAuth = false): ContentPart
69
69
  throw new GatewayError(
70
70
  "platform_unavailable",
71
71
  502,
72
- "Claude response tool_result is missing tool_use_id",
72
+ "Claude response tool_result is missing tool_use_id (request {requestId}). The upstream came back empty-handed — retry 🌙",
73
73
  {},
74
74
  "upstream",
75
75
  );
@@ -185,7 +185,7 @@ export function withClaudeFallbackSignal(
185
185
  return { ...(stopDetails ?? {}), [CANONICAL_SERVED_BY_FALLBACK_KEY]: true };
186
186
  }
187
187
 
188
- export function eventContent(
188
+ function eventContent(
189
189
  content: readonly ContentPart[],
190
190
  sequence: number,
191
191
  ): { events: CanonicalEvent[]; sequence: number } {
@@ -467,7 +467,7 @@ export async function* parseClaudeSseStream(
467
467
  throw new GatewayError(
468
468
  "platform_unavailable",
469
469
  502,
470
- "Malformed Claude SSE event",
470
+ "Malformed Claude SSE event (request {requestId}). The upstream sent something we can't read — retry 🌙",
471
471
  {},
472
472
  "upstream",
473
473
  );
@@ -476,7 +476,7 @@ export async function* parseClaudeSseStream(
476
476
  throw new GatewayError(
477
477
  "platform_unavailable",
478
478
  502,
479
- "Invalid Claude SSE event",
479
+ "Invalid Claude SSE event (request {requestId}). The upstream sent something we can't read — retry 🌙",
480
480
  {},
481
481
  "upstream",
482
482
  );
@@ -492,7 +492,7 @@ export async function* parseClaudeSseStream(
492
492
  throw new GatewayError(
493
493
  "platform_unavailable",
494
494
  502,
495
- "Claude stream ended before message_stop",
495
+ "Claude stream ended before message_stop (request {requestId}). The upstream came back empty-handed — retry 🌙",
496
496
  {},
497
497
  "upstream",
498
498
  );
@@ -14,7 +14,7 @@ export interface OmpTextToolCall {
14
14
  }
15
15
 
16
16
  /** Text remaining after complete tool envelopes are removed. */
17
- export interface OmpTextToolCallResult {
17
+ interface OmpTextToolCallResult {
18
18
  readonly text: string;
19
19
  readonly calls: readonly OmpTextToolCall[];
20
20
  }
@@ -10,7 +10,7 @@ import { gatewayErrorFromStreamError } from "../stream-error-frames";
10
10
  import { usageFromProvider, readResponsesReasoningDelta } from "../../providers/provider-usage";
11
11
  import { createToolEmitLedger } from "../tool-identity";
12
12
 
13
- export function mapResponsesStopReason(
13
+ function mapResponsesStopReason(
14
14
  status: unknown,
15
15
  hasToolCall: boolean,
16
16
  ): "stop" | "length" | "tool_use" | "error" | undefined {
@@ -336,8 +336,8 @@ export async function* decodeResponsesSseStream(
336
336
  "platform_unavailable",
337
337
  502,
338
338
  error instanceof Error
339
- ? `Malformed SSE event: ${error.message}`
340
- : "Malformed SSE event",
339
+ ? `Malformed SSE event: ${error.message} (request {requestId}). The upstream sent something we can't read — retry 🌙`
340
+ : `Malformed SSE event (request {requestId}). The upstream sent something we can't read — retry 🌙`,
341
341
  {},
342
342
  "upstream",
343
343
  );
@@ -31,7 +31,7 @@ export interface SseEvent {
31
31
  readonly data: string;
32
32
  }
33
33
 
34
- export interface SseDecodeOptions {
34
+ interface SseDecodeOptions {
35
35
  /**
36
36
  * Max UTF-16 length of a single line; default equals `maxEventBytes`.
37
37
  *
@@ -51,20 +51,57 @@ export interface SseDecodeOptions {
51
51
 
52
52
  const DEFAULT_MAX_EVENT_BYTES = 4 * 1024 * 1024;
53
53
 
54
+ /**
55
+ * True when the accumulated data is a complete JSON value.
56
+ *
57
+ * Uses a bracket-depth scan that skips string contents (including escapes), so
58
+ * an inline `\n` or `\n\n` inside a string does not count as a structural
59
+ * terminator. A non-JSON payload (`[DONE]`, plain text) has depth 0 and reads
60
+ * as complete, so blank-line boundaries stay working for ordinary streams.
61
+ */
62
+ function isCompleteJsonValue(data: string): boolean {
63
+ if (data.length === 0) return true;
64
+ let depth = 0;
65
+ let inString = false;
66
+ let escaped = false;
67
+ for (let i = 0; i < data.length; i += 1) {
68
+ const ch = data[i]!;
69
+ if (inString) {
70
+ if (escaped) {
71
+ escaped = false;
72
+ } else if (ch === "\\") {
73
+ escaped = true;
74
+ } else if (ch === '"') {
75
+ inString = false;
76
+ }
77
+ continue;
78
+ }
79
+ if (ch === '"') {
80
+ inString = true;
81
+ } else if (ch === "{" || ch === "[") {
82
+ depth += 1;
83
+ } else if (ch === "}" || ch === "]") {
84
+ depth -= 1;
85
+ if (depth < 0) return true; // trailing close: not a partial JSON prefix
86
+ }
87
+ }
88
+ return depth <= 0;
89
+ }
90
+
54
91
  // A corrupt SSE stream is an upstream protocol failure, not a client mistake:
55
92
  // labelling it `invalid_request` told the client its request was wrong and
56
93
  // invited a retry of the identical request against a broken upstream. The
57
94
  // `upstream` origin keeps the public envelope from prefixing it
58
95
  // a gateway-branded message, which blamed the gateway for the provider's bytes.
59
96
  function oversized(what: string): GatewayError {
60
- return new GatewayError("platform_unavailable", 502, `SSE ${what} exceeds size bound`, {}, "upstream");
97
+ return new GatewayError("platform_unavailable", 502, `SSE ${what} exceeds size bound (request {requestId}). The upstream sent more than we can hold — retry 🌙`, {}, "upstream");
61
98
  }
62
99
 
63
100
  export async function* decodeSseEvents(
64
101
  body: ReadableStream<Uint8Array> | null,
65
102
  opts: SseDecodeOptions = {},
66
103
  ): AsyncIterable<SseEvent> {
67
- if (body === null) throw new GatewayError("platform_unavailable", 502, "SSE response has no body", {}, "upstream");
104
+ if (body === null) throw new GatewayError("platform_unavailable", 502, "SSE response has no body (request {requestId}). The upstream came back empty-handed — retry 🌙", {}, "upstream");
68
105
  const maxEventBytes = opts.maxEventBytes ?? DEFAULT_MAX_EVENT_BYTES;
69
106
  const maxLineBytes = opts.maxLineBytes ?? maxEventBytes;
70
107
  const signal = opts.signal;
@@ -156,6 +193,18 @@ export async function* decodeSseEvents(
156
193
  }
157
194
  if (line.startsWith("id:") || line.startsWith("retry:")) return;
158
195
  if (line.trim() === "") {
196
+ // A blank line is an SSE event boundary — but only when the payload
197
+ // accumulated so far is already a complete value. Some upstreams
198
+ // (Qoder IDE /model/v1) emit JSON that contains literal blank lines
199
+ // inside a string (`raw_usage` carries keys like
200
+ // `"data_policy_preferre\n\nnd_count"`). Treating that blank line as a
201
+ // boundary splits the JSON, leaves a dangling continuation, and throws
202
+ // "Malformed SSE line". When the open data is not yet a complete JSON
203
+ // value, append the blank line as a continuation instead.
204
+ if (dataLines.length > 0 && !isCompleteJsonValue(dataLines.join("\n"))) {
205
+ dataLines.push("");
206
+ return;
207
+ }
159
208
  yield* dispatchEvent();
160
209
  return;
161
210
  }
@@ -163,7 +212,18 @@ export async function* decodeSseEvents(
163
212
  // not 502 the request: only `event:`/`data:` carry payload, the rest is
164
213
  // heartbeat/metadata. Unknown colon-prefixed fields are ignored.
165
214
  if (/^[a-zA-Z0-9_-]+:/.test(line)) return;
166
- throw new GatewayError("platform_unavailable", 502, "Malformed SSE line", {}, "upstream");
215
+ // Some vendors (Qoder IDE /model/v1) emit a single `data:` event whose JSON
216
+ // payload contains literal newlines, so the event arrives as several lines:
217
+ // the first has the `data:` prefix, the rest are bare continuations. Treat
218
+ // a bare line as a continuation of the open `data:` event rather than a
219
+ // malformed stream. A bare line with no open data is still an error.
220
+ if (dataLines.length > 0) {
221
+ dataLines.push(line);
222
+ eventBytes += line.length;
223
+ if (eventBytes > maxEventBytes) throw oversized("event");
224
+ return;
225
+ }
226
+ throw new GatewayError("platform_unavailable", 502, "Malformed SSE line (request {requestId}). The upstream sent something we can't read — retry 🌙", {}, "upstream");
167
227
  }
168
228
 
169
229
  try {
@@ -35,14 +35,14 @@ const WIRE_STOP_REASONS = {
35
35
  },
36
36
  } satisfies Record<string, Record<CanonicalStopReason, string>>;
37
37
 
38
- export type StopReasonWireFamily = keyof typeof WIRE_STOP_REASONS;
38
+ type StopReasonWireFamily = keyof typeof WIRE_STOP_REASONS;
39
39
 
40
40
  /**
41
41
  * Maps a canonical stop reason onto `wire`'s vocabulary, falling back when the
42
42
  * terminal carried none: Chat has no null finish_reason and defaults to `stop`,
43
43
  * while Messages omits `stop_reason` entirely.
44
44
  */
45
- export function canonicalStopReasonToWire(
45
+ function canonicalStopReasonToWire(
46
46
  wire: StopReasonWireFamily,
47
47
  reason: CanonicalStopReason | undefined,
48
48
  fallback: string,