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
@@ -52,13 +52,13 @@ export type EmptyStreamFetch = (
52
52
  * unjittered, so a provider returning 200-with-no-events could drive an
53
53
  * unbounded wait, and concurrent clients could resynchronize their retries.
54
54
  */
55
- export interface EmptyStreamPolicy {
55
+ interface EmptyStreamPolicy {
56
56
  readonly maxEmptyReplays?: number;
57
57
  readonly firstEventTimeoutMs?: number;
58
58
  readonly sleep?: (ms: number, signal: AbortSignal) => Promise<void>;
59
59
  }
60
60
 
61
- export interface EmptyStreamResult {
61
+ interface EmptyStreamResult {
62
62
  readonly response: Response;
63
63
  readonly tally: StreamTally;
64
64
  readonly endpoint: string;
@@ -107,18 +107,17 @@ export async function fetchWithEmptyStreamRecovery(
107
107
  const sleep = deps.policy?.sleep ?? abortableSleep;
108
108
 
109
109
  if (endpoints.length === 0) {
110
- throw new GatewayError("platform_unavailable", 502, "No endpoints available", {}, "upstream");
110
+ throw new GatewayError("platform_unavailable", 502, "No endpoints available (request {requestId}). The upstream came back empty-handed — retry 🌙", {}, "upstream");
111
111
  }
112
112
 
113
113
  for (let endpointIndex = 0; endpointIndex < endpoints.length; endpointIndex++) {
114
114
  const endpoint = endpoints[endpointIndex]!;
115
- const isLastEndpoint = endpointIndex === endpoints.length - 1;
116
- const replaysAllowed = isLastEndpoint ? maxReplays : maxReplays;
115
+ const replaysAllowed = maxReplays;
117
116
 
118
117
  for (let replay = 0; replay <= replaysAllowed; replay++) {
119
118
  // Check abort before each fetch
120
119
  if (deps.signal.aborted) {
121
- throw new GatewayError("transport_closed", 499, "Aborted");
120
+ throw new GatewayError("transport_closed", 499, "The request was aborted (request {requestId}). Send it again whenever ready 🙃");
122
121
  }
123
122
 
124
123
  // Check deadline floor
@@ -128,7 +127,7 @@ export async function fetchWithEmptyStreamRecovery(
128
127
  throw new GatewayError(
129
128
  "platform_unavailable",
130
129
  502,
131
- "Deadline exhausted before empty-stream replay",
130
+ "Deadline exhausted before empty-stream replay (request {requestId}). Some things just take longer 🐢",
132
131
  {},
133
132
  "upstream",
134
133
  );
@@ -167,12 +166,7 @@ export async function fetchWithEmptyStreamRecovery(
167
166
  // Empty — replay if we haven't exhausted this endpoint's budget
168
167
  if (replay < replaysAllowed) {
169
168
  const delay = fallbackRetryDelayMs(replay);
170
- try {
171
- await sleep(delay, deps.signal);
172
- } catch (error) {
173
- if (error instanceof GatewayError) throw error;
174
- throw error;
175
- }
169
+ await sleep(delay, deps.signal);
176
170
  }
177
171
  }
178
172
  }
@@ -181,7 +175,7 @@ export async function fetchWithEmptyStreamRecovery(
181
175
  throw new GatewayError(
182
176
  "platform_unavailable",
183
177
  502,
184
- "All endpoints returned empty streams",
178
+ "All endpoints returned empty streams (request {requestId}). The upstream came back empty-handed — retry 🌙",
185
179
  {},
186
180
  "upstream",
187
181
  );
@@ -196,29 +190,38 @@ async function fetchWithTimeout(
196
190
  signal: AbortSignal,
197
191
  timeoutMs: number,
198
192
  ): Promise<Response> {
199
- // Race the fetch against a first-event timeout
193
+ // Race the fetch against a first-event timeout. The timer and the abort
194
+ // listener are both cleaned up once the race settles, so a successful fetch
195
+ // leaves neither behind.
196
+ let timer: ReturnType<typeof setTimeout> | undefined;
197
+ const onAbort = (): void => {
198
+ if (timer !== undefined) clearTimeout(timer);
199
+ // A client cancellation is the caller's signal; the fetch already races the
200
+ // same abort signal, so rejecting here only surfaces the 499 envelope.
201
+ timeoutPromiseReject(
202
+ new GatewayError("transport_closed", 499, "The request was aborted (request {requestId}). Send it again whenever ready 🙃"),
203
+ );
204
+ };
205
+ let timeoutPromiseReject!: (reason: unknown) => void;
200
206
  const timeoutPromise = new Promise<never>((_, reject) => {
201
- const timer = setTimeout(() => {
207
+ timeoutPromiseReject = reject;
208
+ timer = setTimeout(() => {
202
209
  // A first-event timeout is the gateway waiting on a silent upstream (e.g.
203
210
  // a provider that holds the request in a queue), not the client leaving.
204
211
  // It must surface as a deadline failure, never as a `transport_closed`
205
212
  // 499 — that code is reserved for genuine client cancellations and would
206
213
  // otherwise hide a real upstream wait behind "cancelled by client".
207
- reject(new GatewayError("deadline_exceeded", 504, "First event timeout", {}, "upstream"));
214
+ reject(new GatewayError("deadline_exceeded", 504, "First event timeout (request {requestId}). Some things just take longer 🐢", {}, "upstream"));
208
215
  }, timeoutMs);
209
- signal.addEventListener("abort", () => {
210
- clearTimeout(timer);
211
- reject(new GatewayError("transport_closed", 499, "Aborted"));
212
- }, { once: true });
213
216
  });
214
-
217
+ signal.addEventListener("abort", onAbort, { once: true });
215
218
  try {
216
219
  return await Promise.race([
217
220
  fetch(url, { headers: request.headers, body: request.body, signal }),
218
221
  timeoutPromise,
219
222
  ]);
220
- } catch (error) {
221
- if (error instanceof GatewayError) throw error;
222
- throw error;
223
+ } finally {
224
+ if (timer !== undefined) clearTimeout(timer);
225
+ signal.removeEventListener("abort", onAbort);
223
226
  }
224
227
  }
@@ -20,20 +20,26 @@
20
20
  */
21
21
  import type { ImageGenerationOutcome, ProviderAdapter, ProviderDispatchTarget } from "../../providers/provider-contracts";
22
22
  import { GatewayError } from "../../protocol/gateway-error";
23
- import { resolveCredentialForAccount } from "../../providers/operations/provider-credential-service";
24
23
  import type { ServiceKind } from "../../protocol/canonical-model";
25
24
  import { metrics } from "../../observability/metrics";
26
25
  import type { ProxyRequestState } from "../request/state";
27
26
  import { completeAttempt, estimatedUsage, nativeCompletionBase } from "./attempt-finalize";
28
27
  import { runAttemptLoop } from "./attempt-loop";
29
28
  import { captureProviderRateLimit } from "./ratelimit-capture";
29
+ import {
30
+ hasScope,
31
+ jsonResponse,
32
+ networkFetchContext,
33
+ requireRecordBody,
34
+ resolveAttemptCredential,
35
+ } from "./native-shared";
30
36
 
31
37
  import type { GatewayHandlerDeps } from "./handler-deps";
32
38
 
33
- export type GenerationHandlerDeps = GatewayHandlerDeps;
39
+ type GenerationHandlerDeps = GatewayHandlerDeps;
34
40
 
35
41
  /** Which surface a handler serves. Drives candidate filtering and error copy. */
36
- export type GenerationKind = Extract<ServiceKind, "imageGeneration" | "videoGeneration">;
42
+ type GenerationKind = Extract<ServiceKind, "imageGeneration" | "videoGeneration">;
37
43
 
38
44
  /**
39
45
  * Surfaces the gateway routes but does not model.
@@ -44,7 +50,7 @@ export type GenerationKind = Extract<ServiceKind, "imageGeneration" | "videoGene
44
50
  * is the whole feature: routing, admission, failover, and accounting still
45
51
  * apply, and the upstream's own bytes come back untouched.
46
52
  */
47
- export type PassthroughKind = Extract<ServiceKind, "embedding" | "audioTranscription" | "audioSpeech">;
53
+ type PassthroughKind = Extract<ServiceKind, "embedding" | "audioTranscription" | "audioSpeech">;
48
54
 
49
55
  /** Error-copy noun per surface, so a refusal names what the caller asked for. */
50
56
  const SURFACE_NOUNS: Record<GenerationKind | PassthroughKind, string> = {
@@ -74,13 +80,6 @@ function isMediaKind(kind: GenerationKind | PassthroughKind): kind is Generation
74
80
  */
75
81
  const MAX_ITEMS_PER_REQUEST = 10;
76
82
 
77
- function jsonResponse(payload: unknown, status: number, requestId: string): Response {
78
- return new Response(JSON.stringify(payload), {
79
- status,
80
- headers: { "content-type": "application/json", "x-request-id": requestId },
81
- });
82
- }
83
-
84
83
  /**
85
84
  * Forwards a streaming upstream response to the client and accounts for it.
86
85
  *
@@ -145,12 +144,12 @@ async function passThrough(
145
144
  }
146
145
 
147
146
  /** Builds the handler for one generation surface. */
148
- export function createGenerationHandler(kind: GenerationKind, deps: GenerationHandlerDeps) {
147
+ function createGenerationHandler(kind: GenerationKind, deps: GenerationHandlerDeps) {
149
148
  return createNativeSurfaceHandler(kind, deps);
150
149
  }
151
150
 
152
151
  /** Builds the handler for one routed surface, media or passthrough. */
153
- export function createNativeSurfaceHandler(
152
+ function createNativeSurfaceHandler(
154
153
  kind: GenerationKind | PassthroughKind,
155
154
  deps: GenerationHandlerDeps,
156
155
  ) {
@@ -158,23 +157,18 @@ export function createNativeSurfaceHandler(
158
157
  return async ({ request }: { request: Request }): Promise<Response> => {
159
158
  const state = deps.stateStore.require(request);
160
159
  const authorization = state.authorization;
161
- const body = state.ingressBody;
162
- if (!authorization || body === null || typeof body !== "object" || Array.isArray(body)) {
163
- throw new GatewayError(
164
- "invalid_request",
165
- 400,
166
- `The ${noun} body must be a JSON object. Send one with at least a model and a prompt.`,
167
- );
168
- }
169
- const generation = body as Record<string, unknown>;
160
+ const generation = requireRecordBody(
161
+ state,
162
+ `The ${noun} body must be a JSON object (request {requestId}). Send one with at least a model and a prompt. Check the shape of what you sent — then resend 🪞`,
163
+ );
170
164
  if (typeof generation.model !== "string" || generation.model.trim().length === 0) {
171
- throw new GatewayError("invalid_request", 400, `A model is required: it names the ${noun} provider to use.`);
165
+ throw new GatewayError("invalid_request", 400, `A model is required (request {requestId}): it names the ${noun} provider to use. Check the shape of what you sent — then resend 🪞`);
172
166
  }
173
- if (!authorization.snapshot.scopes?.includes("routing:invoke" as never)) {
167
+ if (!hasScope(authorization?.snapshot.scopes, "routing:invoke")) {
174
168
  throw new GatewayError(
175
- "invalid_request",
169
+ "policy_rejected",
176
170
  403,
177
- `${noun} generation not allowed for this key — enable routing:invoke on the API key`,
171
+ `${noun} generation not allowed for this key (request {requestId}) — enable routing:invoke on the API key. Not allowed by policy — no hard feelings 🫂`,
178
172
  );
179
173
  }
180
174
  // Trust boundary only. Keep accepting `messages` for chat-backed providers
@@ -183,7 +177,7 @@ export function createNativeSurfaceHandler(
183
177
  const hasPrompt = typeof generation.prompt === "string" && generation.prompt.trim().length > 0;
184
178
  const hasMessages = Array.isArray(generation.messages) && generation.messages.length > 0;
185
179
  if (!hasPrompt && !hasMessages) {
186
- throw new GatewayError("invalid_request", 400, "prompt is required");
180
+ throw new GatewayError("invalid_request", 400, "prompt is required (request {requestId}). Check the shape of what you sent — then resend 🪞");
187
181
  }
188
182
  if (generation.n !== undefined) {
189
183
  const n = generation.n;
@@ -191,7 +185,7 @@ export function createNativeSurfaceHandler(
191
185
  throw new GatewayError(
192
186
  "invalid_request",
193
187
  400,
194
- `n must be an integer between 1 and ${MAX_ITEMS_PER_REQUEST}`,
188
+ `n must be an integer between 1 and ${MAX_ITEMS_PER_REQUEST} (request {requestId}). Check the shape of what you sent — then resend 🪞`,
195
189
  );
196
190
  }
197
191
  }
@@ -200,10 +194,15 @@ export function createNativeSurfaceHandler(
200
194
  const prepared = await deps.proxyPreparer.prepareNativeService({
201
195
  model: generation.model,
202
196
  serviceKind: kind,
203
- authorization,
197
+ // `requireRecordBody` already refused a missing authorization; this guard
198
+ // exists only to narrow the type for the code below.
199
+ authorization: authorization!,
204
200
  ...(state.abortController.signal ? { signal: state.abortController.signal } : {}),
205
201
  ...(state.clientUserAgent === undefined ? {} : { clientUserAgent: state.clientUserAgent }),
206
202
  });
203
+ if (!authorization) {
204
+ throw new GatewayError("invalid_request", 400, "authentication required (request {requestId})");
205
+ }
207
206
 
208
207
  return runAttemptLoop<Response, ProviderAdapter>({
209
208
  state,
@@ -219,27 +218,15 @@ export function createNativeSurfaceHandler(
219
218
  candidates: prepared.candidates,
220
219
  tenantId: prepared.authorization.tenantId,
221
220
  strictPoolSelection: false,
222
- exhaustedError: new GatewayError("admission_unavailable", 503, `no eligible ${noun} generation route`),
221
+ exhaustedError: new GatewayError("route_unavailable", 503, `no eligible ${noun} generation route (request {requestId}). The bouncer stepped out — come back shortly 🚪`),
223
222
  prepare: async (candidate) => {
224
- const credential = candidate.provider_account_id
225
- ? await resolveCredentialForAccount(
226
- deps.db,
227
- candidate.provider_id,
228
- candidate.provider_account_id,
229
- deps.oauthRefreshService && deps.resolveOAuthRefresher
230
- ? { refreshService: deps.oauthRefreshService, resolveRefresher: deps.resolveOAuthRefresher }
231
- : undefined,
232
- )
233
- : {
234
- provider_id: candidate.provider_id as ProviderAdapter["provider_id"],
235
- credential_kind: "none" as const,
236
- };
223
+ const credential = await resolveAttemptCredential(deps, candidate);
237
224
  const adapter = await deps.resolveProviderAdapter?.(candidate.provider_id);
238
225
  if (!adapter) {
239
226
  throw new GatewayError(
240
227
  "capability_unsupported",
241
228
  400,
242
- `${noun} generation is unavailable for this provider`,
229
+ `${noun} generation is unavailable for this provider (request {requestId}). Know your tools — this one just isn't in the box 🧰`,
243
230
  );
244
231
  }
245
232
  return { credential, adapter };
@@ -309,14 +296,7 @@ export function createNativeSurfaceHandler(
309
296
  abort_signal: state.abortController.signal,
310
297
  request_headers: inboundHeaders,
311
298
  ...(candidate.user_agent === undefined ? {} : { user_agent: candidate.user_agent }),
312
- ...(deps.networkBindingFactory
313
- ? {
314
- outbound_fetch: deps.networkBindingFactory.fetch(
315
- leases.proxySlot?.poolId,
316
- authorization.tenantId,
317
- ),
318
- }
319
- : {}),
299
+ ...networkFetchContext(deps, leases.proxySlot?.poolId, authorization.tenantId),
320
300
  };
321
301
  // Prefer the provider's own method for this kind; otherwise post the
322
302
  // body to the candidate's endpoint through the adapter, which owns the
@@ -344,7 +324,7 @@ export function createNativeSurfaceHandler(
344
324
  new GatewayError(
345
325
  "capability_unsupported",
346
326
  400,
347
- `${noun} generation is unavailable for this provider`,
327
+ `${noun} generation is unavailable for this provider (request {requestId}). Know your tools — this one just isn't in the box 🧰`,
348
328
  ),
349
329
  )
350
330
  : adapter
@@ -141,7 +141,7 @@ async function acquirePoolSlot(
141
141
  throw new GatewayError(
142
142
  "proxy_pool_unhealthy",
143
143
  503,
144
- "Every proxy pool this route requires is unhealthy or disabled. Repair a pool, or relax the route's pool requirement.",
144
+ "Every proxy pool this route requires is unhealthy or disabled (request {requestId}). Repair a pool, or relax the route's pool requirement. This bridge looks shaky — repair or reroute 🔧",
145
145
  { reason: "no_healthy_pool" },
146
146
  "network",
147
147
  );
@@ -172,7 +172,7 @@ async function acquirePoolSlot(
172
172
  throw new GatewayError(
173
173
  "proxy_pool_unavailable",
174
174
  503,
175
- "No active proxy pool is available because coordination is unavailable.",
175
+ "No active proxy pool is available because coordination is unavailable (request {requestId}). The bouncer stepped out — come back shortly 🚪",
176
176
  { reason: "coordination_unavailable" },
177
177
  "network",
178
178
  );
@@ -219,14 +219,14 @@ async function acquirePoolSlot(
219
219
  failure.reason === "at_capacity" || failure.reason === "provider_at_capacity" ? 429 : 503;
220
220
  const message =
221
221
  failure.reason === "at_capacity"
222
- ? "Every proxy pool for this provider is at its concurrency limit. Retry shortly, or add a pool or raise its capacity."
222
+ ? "Every proxy pool for this provider is at its concurrency limit (request {requestId}). Retry shortly, or add a pool or raise its capacity. The pools are full right now — chill 🌊"
223
223
  : failure.reason === "provider_at_capacity"
224
- ? "Every proxy pool has reached this provider's per-proxy concurrency limit. Retry shortly, raise the limit in the provider's routing settings, or add a pool."
224
+ ? "Every proxy pool has reached this provider's per-proxy concurrency limit (request {requestId}). Retry shortly, raise the limit in the provider's routing settings, or add a pool. The proxies are working overtime — hang tight ⚡️"
225
225
  : failure.reason === "blocked"
226
- ? "All proxy pools are saturated for this provider: the upstream refuses every egress configured for it. Replace a blocked proxy, or add one the upstream has not flagged."
226
+ ? "All proxy pools are saturated for this provider (request {requestId}): the upstream refuses every egress configured for it. Replace a blocked proxy, or add one the upstream has not flagged. All roads blocked — time to find a new route 🗺️"
227
227
  : failure.reason === "cooldown"
228
- ? "Every proxy pool for this provider is cooling down after upstream failures. Retry once the cooldown resets."
229
- : "No active proxy pool is configured for this tenant and provider. Add one, or route this provider through direct egress.";
228
+ ? "Every proxy pool for this provider is cooling down after upstream failures (request {requestId}). Retry once the cooldown resets. The proxies are catching their breath — zzz 💤"
229
+ : "No active proxy pool is configured for this tenant and provider (request {requestId}). Add one, or route this provider through direct egress. No bridge to cross — configure a pool first 🏗️";
230
230
  throw new GatewayError(code, status, message, {
231
231
  pools: failure.pools,
232
232
  // Only measured evidence: a cooldown's real reset instant. The
@@ -279,7 +279,7 @@ export async function acquireAttemptLeases(
279
279
  : await acquirePoolSlot(candidate, source, input.poolSelector, input.strictPoolSelection);
280
280
  if (slot) partial.proxySlot = slot;
281
281
  if (input.strictPoolSelection && input.signal.aborted)
282
- throw new GatewayError("transport_closed", 499, "request was cancelled");
282
+ throw new GatewayError("transport_closed", 499, "The request was cancelled (request {requestId}). Send it again whenever ready 🙃");
283
283
  partial.reservation = await source.routingEngine.reserve({
284
284
  ...source.plan,
285
285
  candidates: [candidate],
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Shared helpers for the native dispatch handlers (web search, token count,
3
+ * System One, responses-compact, generation). The handlers previously spelled
4
+ * the same credential-resolution, outbound-fetch, body-validation, thinking-
5
+ * suffix, response-serialization, and usage-clone blocks by hand, and the
6
+ * copies could drift (generation's `jsonResponse` lost the `cache-control`
7
+ * header). One home here keeps them in step.
8
+ */
9
+ import { GatewayError } from "../../protocol/gateway-error";
10
+ import type { ProviderId, ResolvedCredential, ValidatedOutboundFetch } from "../../providers/provider-contracts";
11
+ import { resolveCredentialForAccount } from "../../providers/operations/provider-credential-service";
12
+ import { repriceUsage, usageFromProvider } from "../../providers/provider-usage";
13
+ import type { UsageRecord } from "../../protocol/canonical-model";
14
+ import { parseThinkingSuffix } from "../../protocol/translation/thinking";
15
+ import type { GatewayHandlerDeps } from "./handler-deps";
16
+
17
+ /**
18
+ * Whether a scope list contains the named scope. The native handlers used to
19
+ * force `scopes?.includes(name as never)` — a type-loop hole that bypassed
20
+ * the `AccessScope` union only to compare runtime strings. The check is the
21
+ * same, with a real `readonly string[]` receiver.
22
+ */
23
+ export function hasScope(scopes: readonly string[] | undefined, name: string): boolean {
24
+ return scopes?.includes(name) === true;
25
+ }
26
+
27
+ /**
28
+ * Resolves the credential for one candidate: a real account credential when
29
+ * the candidate carries an account, otherwise the credential-less `"none"`
30
+ * credential. Shared by every native handler's `prepare`.
31
+ */
32
+ export async function resolveAttemptCredential(
33
+ deps: Pick<GatewayHandlerDeps, "db" | "oauthRefreshService" | "resolveOAuthRefresher">,
34
+ candidate: { readonly provider_id: string; readonly provider_account_id?: string },
35
+ ): Promise<ResolvedCredential> {
36
+ if (!candidate.provider_account_id) {
37
+ return {
38
+ provider_id: candidate.provider_id as ProviderId,
39
+ credential_kind: "none" as const,
40
+ };
41
+ }
42
+ return resolveCredentialForAccount(
43
+ deps.db,
44
+ candidate.provider_id,
45
+ candidate.provider_account_id,
46
+ deps.oauthRefreshService && deps.resolveOAuthRefresher
47
+ ? { refreshService: deps.oauthRefreshService, resolveRefresher: deps.resolveOAuthRefresher }
48
+ : undefined,
49
+ );
50
+ }
51
+
52
+ /** The outbound-fetch context fragment native handlers spread into the dispatch context. */
53
+ export function networkFetchContext(
54
+ deps: { readonly networkBindingFactory?: GatewayHandlerDeps["networkBindingFactory"] },
55
+ poolId: string | undefined,
56
+ tenantId: string | null,
57
+ ): { outbound_fetch?: ValidatedOutboundFetch } {
58
+ if (!deps.networkBindingFactory) return {};
59
+ return { outbound_fetch: deps.networkBindingFactory.fetch(poolId, tenantId ?? undefined) };
60
+ }
61
+
62
+ /** Serializes a JSON payload with the shared native-response header set. */
63
+ export function jsonResponse(payload: unknown, status: number, requestId: string): Response {
64
+ return new Response(JSON.stringify(payload), {
65
+ status,
66
+ headers: {
67
+ "content-type": "application/json",
68
+ "cache-control": "no-store",
69
+ "x-request-id": requestId,
70
+ },
71
+ });
72
+ }
73
+
74
+ /**
75
+ * Strips a thinking suffix (`model(high)`) from the model name and rebuilds
76
+ * the body with the bare model when it changed. Returns the bare model and
77
+ * the body to forward upstream.
78
+ */
79
+ export function stripThinkingSuffix(
80
+ model: string,
81
+ body: Record<string, unknown>,
82
+ ): { bareModel: string; body: Record<string, unknown> } {
83
+ const { model: bareModel } = parseThinkingSuffix(model);
84
+ return { bareModel, body: bareModel === model ? body : { ...body, model: bareModel } };
85
+ }
86
+
87
+ /**
88
+ * Validates the ingress body is a non-null object and returns it as a record.
89
+ * Throws the standard `invalid_request` with the caller's message (kept
90
+ * per-route so the wire text is unchanged).
91
+ */
92
+ export function requireRecordBody(
93
+ state: { readonly authorization?: unknown; readonly ingressBody?: unknown },
94
+ message: string,
95
+ ): Record<string, unknown> {
96
+ const { authorization, ingressBody } = state;
97
+ if (!authorization || ingressBody === null || typeof ingressBody !== "object" || Array.isArray(ingressBody))
98
+ throw new GatewayError("invalid_request", 400, message);
99
+ return ingressBody as Record<string, unknown>;
100
+ }
101
+
102
+ /**
103
+ * Reads usage from an upstream response clone, repricing the reported value (or
104
+ * the caller's estimate) for the routed provider/model. Shared by the native
105
+ * handlers that proxy an upstream JSON body verbatim.
106
+ */
107
+ export async function usageFromUpstreamClone(
108
+ response: Response,
109
+ estimate: UsageRecord,
110
+ providerId: string,
111
+ modelId: string,
112
+ ): Promise<UsageRecord> {
113
+ let reportedUsage: ReturnType<typeof usageFromProvider>;
114
+ try {
115
+ const parsed: unknown = await response.clone().json();
116
+ reportedUsage = usageFromProvider(
117
+ typeof parsed === "object" && parsed !== null
118
+ ? (parsed as Record<string, unknown>)["usage"]
119
+ : undefined,
120
+ );
121
+ } catch {
122
+ reportedUsage = undefined;
123
+ }
124
+ return repriceUsage(reportedUsage ?? estimate, providerId, modelId);
125
+ }
126
+
127
+
@@ -41,7 +41,7 @@ import type { PreparedProxyRequest } from "../request/preparer";
41
41
 
42
42
  import type { GatewayHandlerDeps } from "./handler-deps";
43
43
 
44
- export type ProviderProxyHandlerDeps = GatewayHandlerDeps & {
44
+ type ProviderProxyHandlerDeps = GatewayHandlerDeps & {
45
45
  /** Live lookup of a provider's SSRF-validated upstream host. */
46
46
  readonly byokUpstreamHosts?: { readonly get: (providerId: string) => ByokUpstreamHost | undefined };
47
47
  };
@@ -116,7 +116,7 @@ export async function handleProviderProxyRequest(
116
116
  const state = deps.stateStore.require(request);
117
117
  const prepared = state.preparedRequest;
118
118
  if (!prepared)
119
- throw new GatewayError("admission_unavailable", 503, "The request was not prepared, so no route can be dialed. Resend the request.");
119
+ throw new GatewayError("route_unavailable", 503, "The request was not prepared, so no route can be dialed (request {requestId}). Resend the request. The bouncer stepped out — resend shortly 🚪");
120
120
  const canonicalRequest = await applyTenantPreferences(prepared, deps.db);
121
121
  // Tenant system-prompt injection, resolved once per request. For providers
122
122
  // whose upstream validates the fixed system prompt as a channel identity
@@ -135,6 +135,7 @@ export async function handleProviderProxyRequest(
135
135
  {
136
136
  providerId: candidateForPrompt.provider_id,
137
137
  modelId: candidateForPrompt.model_id,
138
+ requestedModel: prepared.plan?.requested_model ?? candidateForPrompt.model_id,
138
139
  },
139
140
  )?.text
140
141
  : undefined;
@@ -188,13 +189,13 @@ export async function handleProviderProxyRequest(
188
189
  tenantId: prepared.authorization.tenantId,
189
190
  strictPoolSelection: true,
190
191
  resolveHost: (candidate) => deps.byokUpstreamHosts?.get(candidate.provider_id),
191
- exhaustedError: new GatewayError("admission_unavailable", 503, "No account could be leased for this request. Retry shortly; if it persists, add an account or check that one is healthy."),
192
+ exhaustedError: new GatewayError("route_unavailable", 503, "No account could be leased for this request (request {requestId}). Retry shortly; if it persists, add an account or check that one is healthy. The bouncer stepped out — come back shortly 🚪"),
192
193
  prepare: async (candidate) => {
193
194
  if (!candidate.provider_account_id && candidate.requires_account !== false)
194
195
  throw new GatewayError(
195
- "admission_unavailable",
196
+ "route_unavailable",
196
197
  503,
197
- "This route has no usable credential. Save one on the account, or route to a provider whose account is configured.",
198
+ "This route has no usable credential (request {requestId}). Save one on the account, or route to a provider whose account is configured. Wrong key, right door — check the credential 🔑",
198
199
  );
199
200
  const credential = candidate.provider_account_id
200
201
  ? await resolveCredentialForAccount(
@@ -211,13 +212,13 @@ export async function handleProviderProxyRequest(
211
212
  };
212
213
  const adapter = await deps.resolveProviderAdapter?.(candidate.provider_id);
213
214
  if (!adapter)
214
- throw new GatewayError("admission_unavailable", 503, "provider adapter not registered");
215
+ throw new GatewayError("route_unavailable", 503, "provider adapter not registered (request {requestId}). Even we have bad days — check the registry wiring 🐻");
215
216
  if (!isBundledProviderId(candidate.provider_id)) {
216
217
  if (!deps.byokUpstreamHosts?.get(candidate.provider_id) || !deps.networkBindingFactory)
217
218
  throw new GatewayError(
218
- "admission_unavailable",
219
+ "route_unavailable",
219
220
  503,
220
- "validated network binding is required for configurable upstreams",
221
+ "validated network binding is required for configurable upstreams (request {requestId}). No bridge to cross — configure a pool first 🏗️",
221
222
  );
222
223
  }
223
224
  return { credential, adapter };
@@ -239,6 +240,7 @@ export async function handleProviderProxyRequest(
239
240
  ...(candidate.output_limit == null ? {} : { output_limit: candidate.output_limit }),
240
241
  capabilities: candidate.capability_profile,
241
242
  user_agent: candidate.user_agent,
243
+ ...(candidate.dialect === undefined ? {} : { dialect: candidate.dialect }),
242
244
  };
243
245
  const candidateRequest =
244
246
  candidate.model_id === dispatchRequest.model