cartethyia 2.7.3 → 2.7.4

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 (352) hide show
  1. package/dist/dashboard/assets/{BannedPage-2TJD37Hb.js → BannedPage-ChG5svFb.js} +1 -1
  2. package/dist/dashboard/assets/BannedPage-ChG5svFb.js.gz +0 -0
  3. package/dist/dashboard/assets/{CliToolDetailPage-xefJNRmJ.js → CliToolDetailPage-BIcWQun7.js} +1 -1
  4. package/dist/dashboard/assets/CliToolDetailPage-BIcWQun7.js.gz +0 -0
  5. package/dist/dashboard/assets/{CliToolsPage-Rj9iHQ3F.js → CliToolsPage-Bf2VelDe.js} +1 -1
  6. package/dist/dashboard/assets/CliToolsPage-Bf2VelDe.js.gz +0 -0
  7. package/dist/dashboard/assets/{CombosPage-JEbYJFxw.js → CombosPage-DCgm5g6g.js} +1 -1
  8. package/dist/dashboard/assets/CombosPage-DCgm5g6g.js.gz +0 -0
  9. package/dist/dashboard/assets/ConsoleLogPage-a8mD0QF1.js +1 -0
  10. package/dist/dashboard/assets/ConsoleLogPage-a8mD0QF1.js.gz +0 -0
  11. package/dist/dashboard/assets/CustomizationPage-ggeMSjte.js +1 -0
  12. package/dist/dashboard/assets/CustomizationPage-ggeMSjte.js.gz +0 -0
  13. package/dist/dashboard/assets/HealthEventsModal-DzSWzE_j.js +1 -0
  14. package/dist/dashboard/assets/HealthEventsModal-DzSWzE_j.js.gz +0 -0
  15. package/dist/dashboard/assets/{LoginPage-C4w7vea-.js → LoginPage-U6AKaJcF.js} +1 -1
  16. package/dist/dashboard/assets/LoginPage-U6AKaJcF.js.gz +0 -0
  17. package/dist/dashboard/assets/{ModelPicker-BtPcAfot.js → ModelPicker-BMS2t8BA.js} +1 -1
  18. package/dist/dashboard/assets/ModelPicker-BMS2t8BA.js.gz +0 -0
  19. package/dist/dashboard/assets/OverviewPage-D2D4IiHl.js +1 -0
  20. package/dist/dashboard/assets/OverviewPage-D2D4IiHl.js.gz +0 -0
  21. package/dist/dashboard/assets/ProviderDetailPage-CrNf7j_X.js +5 -0
  22. package/dist/dashboard/assets/ProviderDetailPage-CrNf7j_X.js.gz +0 -0
  23. package/dist/dashboard/assets/ProvidersPage-BSDbquc1.js +1 -0
  24. package/dist/dashboard/assets/ProvidersPage-BSDbquc1.js.gz +0 -0
  25. package/dist/dashboard/assets/ProxyPage-lsrx7NYg.js +9 -0
  26. package/dist/dashboard/assets/ProxyPage-lsrx7NYg.js.gz +0 -0
  27. package/dist/dashboard/assets/QuotaPage-L0skzML8.js +1 -0
  28. package/dist/dashboard/assets/QuotaPage-L0skzML8.js.gz +0 -0
  29. package/dist/dashboard/assets/SettingsPage-CmAbjRHW.js +1 -0
  30. package/dist/dashboard/assets/SettingsPage-CmAbjRHW.js.gz +0 -0
  31. package/dist/dashboard/assets/{SetupPage-BQS0Rx_u.js → SetupPage-f0DE1h0-.js} +1 -1
  32. package/dist/dashboard/assets/SetupPage-f0DE1h0-.js.gz +0 -0
  33. package/dist/dashboard/assets/{SortableList-BAL5npBC.js → SortableList-ClzpwyET.js} +1 -1
  34. package/dist/dashboard/assets/SortableList-ClzpwyET.js.gz +0 -0
  35. package/dist/dashboard/assets/{StudioPage-TP7voAQ0.js → StudioPage-Cq5tW7hW.js} +3 -3
  36. package/dist/dashboard/assets/StudioPage-Cq5tW7hW.js.gz +0 -0
  37. package/dist/dashboard/assets/UsagePage-Dfc9rWQn.js +56 -0
  38. package/dist/dashboard/assets/UsagePage-Dfc9rWQn.js.gz +0 -0
  39. package/dist/dashboard/assets/api-keys-C38iXoO4.js +1 -0
  40. package/dist/dashboard/assets/api-keys-C38iXoO4.js.gz +0 -0
  41. package/dist/dashboard/assets/app-a159k0ub.css +2 -0
  42. package/dist/dashboard/assets/app-a159k0ub.css.gz +0 -0
  43. package/dist/dashboard/assets/assert-DbRA8EG2.js +15 -0
  44. package/dist/dashboard/assets/assert-DbRA8EG2.js.gz +0 -0
  45. package/dist/dashboard/assets/{badge-B_DMpMJN.js → badge-D9CS6dZk.js} +1 -1
  46. package/dist/dashboard/assets/badge-D9CS6dZk.js.gz +0 -0
  47. package/dist/dashboard/assets/{banner-DMrMPmth.js → banner-DympBNq4.js} +1 -1
  48. package/dist/dashboard/assets/banner-DympBNq4.js.gz +0 -0
  49. package/dist/dashboard/assets/{card-Be5Uzamx.js → card-ibRWU_uT.js} +1 -1
  50. package/dist/dashboard/assets/card-ibRWU_uT.js.gz +0 -0
  51. package/dist/dashboard/assets/cli-tools-63qBGaRw.js +1 -0
  52. package/dist/dashboard/assets/cli-tools-63qBGaRw.js.gz +0 -0
  53. package/dist/dashboard/assets/{clipboard-button-DOfrVmsi.js → clipboard-button-DJm-9pz7.js} +1 -1
  54. package/dist/dashboard/assets/clipboard-button-DJm-9pz7.js.gz +0 -0
  55. package/dist/dashboard/assets/{dialog-C9omdjQt.js → dialog-8fiE6tec.js} +1 -1
  56. package/dist/dashboard/assets/dialog-8fiE6tec.js.gz +0 -0
  57. package/dist/dashboard/assets/{drawer-Bm8WKaGY.js → drawer-CqGYeOrd.js} +1 -1
  58. package/dist/dashboard/assets/drawer-CqGYeOrd.js.gz +0 -0
  59. package/dist/dashboard/assets/{entry-q5HwZ8nN.js → entry-BSAsYsmV.js} +1 -1
  60. package/dist/dashboard/assets/entry-BSAsYsmV.js.gz +0 -0
  61. package/dist/dashboard/assets/entry-CiIQULZD.js +4 -0
  62. package/dist/dashboard/assets/entry-CiIQULZD.js.gz +0 -0
  63. package/dist/dashboard/assets/entry-Daz0lJ-W.js +1 -0
  64. package/dist/dashboard/assets/entry-Daz0lJ-W.js.gz +0 -0
  65. package/dist/dashboard/assets/{formatters-BTScrQpe.js → formatters-D-MTJxMr.js} +1 -1
  66. package/dist/dashboard/assets/formatters-D-MTJxMr.js.gz +0 -0
  67. package/dist/dashboard/assets/{github-badge-Orjuzemr.js → github-badge-CPK47xQx.js} +1 -1
  68. package/dist/dashboard/assets/github-badge-CPK47xQx.js.gz +0 -0
  69. package/dist/dashboard/assets/{index-cPrX5vn6.js → index-DBNPOfhV.js} +2 -2
  70. package/dist/dashboard/assets/index-DBNPOfhV.js.gz +0 -0
  71. package/dist/dashboard/assets/{inline-Dvux8w-0.js → inline-BT0zntEr.js} +1 -1
  72. package/dist/dashboard/assets/inline-BT0zntEr.js.gz +0 -0
  73. package/dist/dashboard/assets/{input-CU1Rgf3p.js → input-Da2ZViWg.js} +1 -1
  74. package/dist/dashboard/assets/input-Da2ZViWg.js.gz +0 -0
  75. package/dist/dashboard/assets/{layout-C_7A6H55.js → layout-BXNeLw6M.js} +1 -1
  76. package/dist/dashboard/assets/layout-BXNeLw6M.js.gz +0 -0
  77. package/dist/dashboard/assets/{list-row-CzFvogbj.js → list-row-BycOrrIV.js} +1 -1
  78. package/dist/dashboard/assets/list-row-BycOrrIV.js.gz +0 -0
  79. package/dist/dashboard/assets/live-DCQNZpF7.js +1 -0
  80. package/dist/dashboard/assets/live-DCQNZpF7.js.gz +0 -0
  81. package/dist/dashboard/assets/minimize-2-BTmMTK4o.js +1 -0
  82. package/dist/dashboard/assets/minimize-2-BTmMTK4o.js.gz +0 -0
  83. package/dist/dashboard/assets/{mutation-JJGQm8TD.js → mutation-Rr0TIvcj.js} +1 -1
  84. package/dist/dashboard/assets/mutation-Rr0TIvcj.js.gz +0 -0
  85. package/dist/dashboard/assets/network-9Yj2ZyLo.js +1 -0
  86. package/dist/dashboard/assets/network-9Yj2ZyLo.js.gz +0 -0
  87. package/dist/dashboard/assets/{numbers-BNDZnsUE.js → numbers-CEagj6ZZ.js} +2 -2
  88. package/dist/dashboard/assets/numbers-CEagj6ZZ.js.gz +0 -0
  89. package/dist/dashboard/assets/{query-keys-CLaNz8Xz.js → query-keys-BloKtaXJ.js} +1 -1
  90. package/dist/dashboard/assets/query-keys-BloKtaXJ.js.gz +0 -0
  91. package/dist/dashboard/assets/quota-Caw7wqci.js +1 -0
  92. package/dist/dashboard/assets/quota-Caw7wqci.js.gz +0 -0
  93. package/dist/dashboard/assets/routing-CO9zCUus.js +1 -0
  94. package/dist/dashboard/assets/routing-CO9zCUus.js.gz +0 -0
  95. package/dist/dashboard/assets/{select-B3iTetHN.js → select-BIYIBkMU.js} +2 -2
  96. package/dist/dashboard/assets/select-BIYIBkMU.js.gz +0 -0
  97. package/dist/dashboard/assets/settings-jS3-PNSA.js +1 -0
  98. package/dist/dashboard/assets/settings-jS3-PNSA.js.gz +0 -0
  99. package/dist/dashboard/assets/{stack-KrOrm7JZ.js → stack-DfgnFDWY.js} +1 -1
  100. package/dist/dashboard/assets/stack-DfgnFDWY.js.gz +0 -0
  101. package/dist/dashboard/assets/{state-DLS7IDMy.js → state-CkfdYWel.js} +1 -1
  102. package/dist/dashboard/assets/state-CkfdYWel.js.gz +0 -0
  103. package/dist/dashboard/assets/{switch-42ItvbAs.js → switch-SYm237HG.js} +1 -1
  104. package/dist/dashboard/assets/switch-SYm237HG.js.gz +0 -0
  105. package/dist/dashboard/assets/{useMutation-Dmnfh1Ue.js → useMutation-BQqCRNIi.js} +1 -1
  106. package/dist/dashboard/assets/useMutation-BQqCRNIi.js.gz +0 -0
  107. package/dist/dashboard/index.html +2 -2
  108. package/dist/dashboard/index.html.gz +0 -0
  109. package/dist/dashboard/precompressed-assets.json +55 -54
  110. package/migrations/full-postgres/0027_qoder_identity_genuine_seed.sql +9 -10
  111. package/migrations/full-postgres/0032_add_provider_pool_accounts.sql +53 -0
  112. package/migrations/full-postgres/0033_add_api_key_ban.sql +9 -0
  113. package/migrations/full-postgres/0034_add_api_key_ban_until.sql +7 -0
  114. package/migrations/full-postgres/0035_align_pool_account_status_enum.sql +21 -0
  115. package/migrations/lite-sqlite3/0027_qoder_identity_genuine_seed.sql +9 -10
  116. package/migrations/lite-sqlite3/0032_add_provider_pool_accounts.sql +44 -0
  117. package/migrations/lite-sqlite3/0033_add_api_key_ban.sql +8 -0
  118. package/migrations/lite-sqlite3/0034_add_api_key_ban_until.sql +5 -0
  119. package/migrations/lite-sqlite3/0035_align_pool_account_status_enum.sql +6 -0
  120. package/package.json +2 -1
  121. package/src/boot/app.ts +9 -8
  122. package/src/config/network.ts +7 -3
  123. package/src/console/api-keys/contracts.ts +52 -0
  124. package/src/console/api-keys/routes.ts +93 -4
  125. package/src/console/audit/routes.ts +3 -3
  126. package/src/console/auth/account-reset.ts +4 -4
  127. package/src/console/auth/session.ts +3 -3
  128. package/src/console/backup/contracts.ts +2 -0
  129. package/src/console/backup/export-endpoint.ts +6 -6
  130. package/src/console/backup/routes.ts +3 -3
  131. package/src/console/cli-tools/routes.ts +3 -3
  132. package/src/console/console-router-guards.ts +2 -2
  133. package/src/console/console-router.ts +4 -4
  134. package/src/console/data-management/routes.ts +3 -3
  135. package/src/console/domain-registration.ts +2 -2
  136. package/src/console/observability/contracts.ts +4 -0
  137. package/src/console/observability/live.ts +3 -3
  138. package/src/console/observability/logs.ts +3 -3
  139. package/src/console/observability/performance.ts +3 -3
  140. package/src/console/observability/routes.ts +3 -3
  141. package/src/console/observability/store.ts +16 -16
  142. package/src/console/providers/catalog/contracts.ts +43 -22
  143. package/src/console/providers/catalog/provider-operations.ts +17 -2
  144. package/src/console/providers/catalog/routes.ts +3 -3
  145. package/src/console/providers/catalog/store-helpers.ts +349 -326
  146. package/src/console/providers/catalog/store.ts +1523 -1292
  147. package/src/console/providers/detail/routes.ts +3 -3
  148. package/src/console/providers/oauth/routes.ts +3 -3
  149. package/src/console/quota/account-quota-tenant.ts +26 -1
  150. package/src/console/quota/account-test-egress.ts +4 -4
  151. package/src/console/routing/model/routes.ts +3 -3
  152. package/src/console/routing/pools/contracts.ts +11 -8
  153. package/src/console/routing/pools/operations.ts +14 -7
  154. package/src/console/routing/pools/probe-verdict.ts +8 -3
  155. package/src/console/routing/pools/relay-deploy.ts +9 -9
  156. package/src/console/routing/pools/routes.ts +3 -3
  157. package/src/console/routing/pools/store.ts +21 -1
  158. package/src/console/settings/routes.ts +3 -3
  159. package/src/console/share/share-router.ts +12 -12
  160. package/src/console/share/share-stats.ts +105 -35
  161. package/src/console/shared/read-through-cache.ts +38 -2
  162. package/src/console/updates/routes.ts +7 -4
  163. package/src/network/outbound-fetch.ts +75 -4
  164. package/src/network/pool/resolver.ts +39 -39
  165. package/src/network/pool-cooldown-store.ts +71 -16
  166. package/src/network/pool-health-machine.ts +21 -1
  167. package/src/network/rate-limit-verdict.ts +68 -0
  168. package/src/network/ssrf.ts +4 -4
  169. package/src/observability/event-query.ts +320 -16
  170. package/src/observability/telemetry-status.ts +1 -1
  171. package/src/persistence/db-sqlite.ts +7 -2
  172. package/src/persistence/readiness.ts +18 -6
  173. package/src/persistence/schema.ts +66 -7
  174. package/src/persistence/stores/api-key-store.ts +99 -3
  175. package/src/persistence/stores/telemetry-store.ts +1 -1
  176. package/src/protocol/codecs.ts +6 -6
  177. package/src/protocol/gateway-error.ts +6 -3
  178. package/src/protocol/image-generation.ts +2 -2
  179. package/src/protocol/response/chat.ts +2 -2
  180. package/src/protocol/response/codex.ts +1 -1
  181. package/src/protocol/response/gemini.ts +1 -1
  182. package/src/protocol/response/messages.ts +5 -5
  183. package/src/protocol/response/responses.ts +2 -2
  184. package/src/protocol/sse-decoder.ts +14 -3
  185. package/src/protocol/translation/capabilities.ts +2 -2
  186. package/src/protocol/upstream/send-claude-messages.ts +3 -3
  187. package/src/providers/adapters/base-adapter.ts +1 -1
  188. package/src/providers/adapters/compatible-adapter.ts +2 -2
  189. package/src/providers/authentication/oauth-refresh-service.ts +2 -2
  190. package/src/providers/configured-provider.ts +1 -1
  191. package/src/providers/discovery/probe-phases.ts +2 -2
  192. package/src/providers/discovery/probing-service.ts +12 -12
  193. package/src/providers/integrations/agentrouter/agentrouter.ts +2 -3
  194. package/src/providers/integrations/alibabacp/alibabacp.ts +2 -2
  195. package/src/providers/integrations/anthropic.ts +4 -11
  196. package/src/providers/integrations/antigravity/antigravity.ts +13 -24
  197. package/src/providers/integrations/buddy/buddy-catalog-shared.ts +1 -1
  198. package/src/providers/integrations/buddy/buddy-discovery-shared.ts +37 -6
  199. package/src/providers/integrations/buddy/buddy-identity.ts +22 -1
  200. package/src/providers/integrations/buddy/buddy-media.ts +16 -16
  201. package/src/providers/integrations/buddy/codebuddy/provider.ts +1 -1
  202. package/src/providers/integrations/buddy/workbuddy/provider.ts +1 -1
  203. package/src/providers/integrations/claude/claude-betas.ts +1 -1
  204. package/src/providers/integrations/claude/claude-credentials.ts +1 -1
  205. package/src/providers/integrations/claude/claude-slow-lane-retry.ts +1 -1
  206. package/src/providers/integrations/claude/claude.ts +3 -10
  207. package/src/providers/integrations/cline/cline.ts +1 -1
  208. package/src/providers/integrations/cloudflareai/cloudflareai.ts +4 -7
  209. package/src/providers/integrations/codex/codex-errors.ts +1 -1
  210. package/src/providers/integrations/codex/codex.ts +14 -15
  211. package/src/providers/integrations/commandcode/commandcode.ts +4 -4
  212. package/src/providers/integrations/commandcode/quota.ts +5 -1
  213. package/src/providers/integrations/cursor/cursor-http2.ts +11 -11
  214. package/src/providers/integrations/cursor/cursor.ts +15 -15
  215. package/src/providers/integrations/factory/factory.ts +4 -4
  216. package/src/providers/integrations/gemini.ts +11 -11
  217. package/src/providers/integrations/github/github.ts +6 -6
  218. package/src/providers/integrations/grok/grok.ts +2 -6
  219. package/src/providers/integrations/inferhub.ts +4 -11
  220. package/src/providers/integrations/kimi/kimi.ts +4 -11
  221. package/src/providers/integrations/kiro/kiro.ts +10 -10
  222. package/src/providers/integrations/muse/muse.ts +6 -9
  223. package/src/providers/integrations/perplexity.ts +2 -2
  224. package/src/providers/integrations/qoder/qoder-catalog.ts +47 -9
  225. package/src/providers/integrations/qoder/qoder-checkin.ts +26 -8
  226. package/src/providers/integrations/qoder/qoder-native-identity-store.ts +9 -8
  227. package/src/providers/integrations/qoder/qoder-realm.ts +15 -0
  228. package/src/providers/integrations/qoder/qoder-umid.ts +12 -13
  229. package/src/providers/integrations/qoder/qoder-virtual.ts +2 -2
  230. package/src/providers/integrations/qoder/qoder.ts +188 -22
  231. package/src/providers/integrations/tokenharbor.ts +2 -2
  232. package/src/providers/integrations/trae/trae.ts +6 -9
  233. package/src/providers/integrations/zcode/oauth.ts +1 -1
  234. package/src/providers/operations/provider-catalog-service.ts +4 -4
  235. package/src/providers/operations/provider-credential-service.ts +16 -4
  236. package/src/providers/operations/upstream-deadline.ts +3 -3
  237. package/src/providers/provider-contracts.ts +47 -3
  238. package/src/providers/provider-credential-resolver.ts +1 -1
  239. package/src/providers/provider-registry.ts +4 -4
  240. package/src/providers/quota/quota-contracts.ts +24 -10
  241. package/src/providers/quota/refresh.ts +2 -2
  242. package/src/providers/search/search-provider.ts +5 -5
  243. package/src/security/abuse.ts +4 -4
  244. package/src/security/admission/reasons.ts +17 -17
  245. package/src/security/api-key-auth.ts +4 -4
  246. package/src/shared/coerce.ts +19 -0
  247. package/src/transport/dispatch/attempt-finalize.ts +4 -3
  248. package/src/transport/dispatch/attempt-loop.ts +10 -7
  249. package/src/transport/dispatch/empty-stream.ts +6 -6
  250. package/src/transport/dispatch/generation.ts +9 -9
  251. package/src/transport/dispatch/leases.ts +8 -8
  252. package/src/transport/dispatch/proxy-request.ts +7 -7
  253. package/src/transport/dispatch/responses-compact.ts +11 -11
  254. package/src/transport/dispatch/retry-policy.ts +4 -67
  255. package/src/transport/dispatch/stream-bridge.ts +1 -1
  256. package/src/transport/dispatch/streaming/finalize.ts +9 -8
  257. package/src/transport/dispatch/systemone.ts +8 -8
  258. package/src/transport/dispatch/token-count.ts +9 -9
  259. package/src/transport/dispatch/websearch.ts +6 -6
  260. package/src/transport/drain-endpoint.ts +2 -2
  261. package/src/transport/failure-policy.ts +13 -6
  262. package/src/transport/guards/auth-guard.ts +6 -5
  263. package/src/transport/guards/body-policy.ts +4 -4
  264. package/src/transport/guards/error-lifecycle.ts +18 -3
  265. package/src/transport/guards/guard-errors.ts +7 -3
  266. package/src/transport/guards/headroom-guard.ts +1 -1
  267. package/src/transport/guards/pipeline.ts +6 -6
  268. package/src/transport/guards/readiness-guard.ts +16 -2
  269. package/src/transport/guards/request-context.ts +3 -3
  270. package/src/transport/request/preparer.ts +20 -20
  271. package/src/transport/request/state.ts +4 -7
  272. package/src/transport/routing/route-catalog.ts +97 -2
  273. package/src/transport/routing/route-model.ts +7 -7
  274. package/src/transport/routing/router.ts +2 -2
  275. package/src/transport/shutdown-notice.ts +19 -7
  276. package/src/transport/surface/completion.ts +1 -1
  277. package/src/transport/surface/messages/encode.ts +1 -1
  278. package/dist/dashboard/assets/BannedPage-2TJD37Hb.js.gz +0 -0
  279. package/dist/dashboard/assets/CliToolDetailPage-xefJNRmJ.js.gz +0 -0
  280. package/dist/dashboard/assets/CliToolsPage-Rj9iHQ3F.js.gz +0 -0
  281. package/dist/dashboard/assets/CombosPage-JEbYJFxw.js.gz +0 -0
  282. package/dist/dashboard/assets/ConsoleLogPage-C3xx_inM.js +0 -1
  283. package/dist/dashboard/assets/ConsoleLogPage-C3xx_inM.js.gz +0 -0
  284. package/dist/dashboard/assets/CustomizationPage-Dw306gJU.js +0 -1
  285. package/dist/dashboard/assets/CustomizationPage-Dw306gJU.js.gz +0 -0
  286. package/dist/dashboard/assets/HealthEventsModal-jaBo1TWV.js +0 -1
  287. package/dist/dashboard/assets/HealthEventsModal-jaBo1TWV.js.gz +0 -0
  288. package/dist/dashboard/assets/LoginPage-C4w7vea-.js.gz +0 -0
  289. package/dist/dashboard/assets/ModelPicker-BtPcAfot.js.gz +0 -0
  290. package/dist/dashboard/assets/OverviewPage-CGC5gNs1.js +0 -1
  291. package/dist/dashboard/assets/OverviewPage-CGC5gNs1.js.gz +0 -0
  292. package/dist/dashboard/assets/ProviderDetailPage-DQcthNGt.js +0 -4
  293. package/dist/dashboard/assets/ProviderDetailPage-DQcthNGt.js.gz +0 -0
  294. package/dist/dashboard/assets/ProvidersPage-0OsXRGXe.js +0 -1
  295. package/dist/dashboard/assets/ProvidersPage-0OsXRGXe.js.gz +0 -0
  296. package/dist/dashboard/assets/ProxyPage-DdPc8slb.js +0 -9
  297. package/dist/dashboard/assets/ProxyPage-DdPc8slb.js.gz +0 -0
  298. package/dist/dashboard/assets/QuotaPage-Ci5O4BnH.js +0 -1
  299. package/dist/dashboard/assets/QuotaPage-Ci5O4BnH.js.gz +0 -0
  300. package/dist/dashboard/assets/SettingsPage-CDZjTt87.js +0 -1
  301. package/dist/dashboard/assets/SettingsPage-CDZjTt87.js.gz +0 -0
  302. package/dist/dashboard/assets/SetupPage-BQS0Rx_u.js.gz +0 -0
  303. package/dist/dashboard/assets/SortableList-BAL5npBC.js.gz +0 -0
  304. package/dist/dashboard/assets/StudioPage-TP7voAQ0.js.gz +0 -0
  305. package/dist/dashboard/assets/UsagePage-Cj_fEzhg.js +0 -56
  306. package/dist/dashboard/assets/UsagePage-Cj_fEzhg.js.gz +0 -0
  307. package/dist/dashboard/assets/api-keys-MbI7e7rt.js +0 -1
  308. package/dist/dashboard/assets/api-keys-MbI7e7rt.js.gz +0 -0
  309. package/dist/dashboard/assets/app-B2vyPoII.css +0 -2
  310. package/dist/dashboard/assets/app-B2vyPoII.css.gz +0 -0
  311. package/dist/dashboard/assets/assert-C0YYuZJ4.js +0 -1
  312. package/dist/dashboard/assets/assert-C0YYuZJ4.js.gz +0 -0
  313. package/dist/dashboard/assets/badge-B_DMpMJN.js.gz +0 -0
  314. package/dist/dashboard/assets/banner-DMrMPmth.js.gz +0 -0
  315. package/dist/dashboard/assets/card-Be5Uzamx.js.gz +0 -0
  316. package/dist/dashboard/assets/cli-tools-muFRphDu.js +0 -1
  317. package/dist/dashboard/assets/cli-tools-muFRphDu.js.gz +0 -0
  318. package/dist/dashboard/assets/clipboard-button-DOfrVmsi.js.gz +0 -0
  319. package/dist/dashboard/assets/dialog-C9omdjQt.js.gz +0 -0
  320. package/dist/dashboard/assets/drawer-Bm8WKaGY.js.gz +0 -0
  321. package/dist/dashboard/assets/entry-DS90gUks.js +0 -1
  322. package/dist/dashboard/assets/entry-DS90gUks.js.gz +0 -0
  323. package/dist/dashboard/assets/entry-j40LJotO.js +0 -4
  324. package/dist/dashboard/assets/entry-j40LJotO.js.gz +0 -0
  325. package/dist/dashboard/assets/entry-q5HwZ8nN.js.gz +0 -0
  326. package/dist/dashboard/assets/formatters-BTScrQpe.js.gz +0 -0
  327. package/dist/dashboard/assets/github-badge-Orjuzemr.js.gz +0 -0
  328. package/dist/dashboard/assets/index-cPrX5vn6.js.gz +0 -0
  329. package/dist/dashboard/assets/inline-Dvux8w-0.js.gz +0 -0
  330. package/dist/dashboard/assets/input-CU1Rgf3p.js.gz +0 -0
  331. package/dist/dashboard/assets/layout-C_7A6H55.js.gz +0 -0
  332. package/dist/dashboard/assets/list-row-CzFvogbj.js.gz +0 -0
  333. package/dist/dashboard/assets/live-KaG15HLQ.js +0 -1
  334. package/dist/dashboard/assets/live-KaG15HLQ.js.gz +0 -0
  335. package/dist/dashboard/assets/minimize-2-D-MN8W8a.js +0 -1
  336. package/dist/dashboard/assets/minimize-2-D-MN8W8a.js.gz +0 -0
  337. package/dist/dashboard/assets/mutation-JJGQm8TD.js.gz +0 -0
  338. package/dist/dashboard/assets/numbers-BNDZnsUE.js.gz +0 -0
  339. package/dist/dashboard/assets/query-keys-CLaNz8Xz.js.gz +0 -0
  340. package/dist/dashboard/assets/quota-DZGyp0N5.js +0 -1
  341. package/dist/dashboard/assets/quota-DZGyp0N5.js.gz +0 -0
  342. package/dist/dashboard/assets/routing-BEH32QOC.js +0 -1
  343. package/dist/dashboard/assets/routing-BEH32QOC.js.gz +0 -0
  344. package/dist/dashboard/assets/select-B3iTetHN.js.gz +0 -0
  345. package/dist/dashboard/assets/settings-Dt3Eqbnq.js +0 -1
  346. package/dist/dashboard/assets/settings-Dt3Eqbnq.js.gz +0 -0
  347. package/dist/dashboard/assets/stack-KrOrm7JZ.js.gz +0 -0
  348. package/dist/dashboard/assets/state-DLS7IDMy.js.gz +0 -0
  349. package/dist/dashboard/assets/switch-42ItvbAs.js.gz +0 -0
  350. package/dist/dashboard/assets/useMutation-Dmnfh1Ue.js.gz +0 -0
  351. /package/dist/dashboard/assets/{app-wvwCi4Eh.js → app-BayTTy01.js} +0 -0
  352. /package/dist/dashboard/assets/{app-wvwCi4Eh.js.gz → app-BayTTy01.js.gz} +0 -0
@@ -49,6 +49,18 @@ function retryAfterSeconds(error: GatewayError | undefined): string | undefined
49
49
  return undefined;
50
50
  }
51
51
 
52
+ /**
53
+ * Ensures a public message carries the request id. Builders write a
54
+ * `{requestId}` placeholder in the lead (the canonical convention); anything
55
+ * that predates the convention gets the id appended so every client-visible
56
+ * message is correlatable to the server log.
57
+ */
58
+ function applyRequestId(message: string, requestId: string): string {
59
+ if (message.includes("{requestId}")) return message.replaceAll("{requestId}", requestId);
60
+ if (message.includes("(request ")) return message;
61
+ return `${message} (request ${requestId})`;
62
+ }
63
+
52
64
  /**
53
65
  * Normalizes every thrown error (from any lifecycle hook or handler, on
54
66
  * `/v1/*` or `/console/api/*`) into the stable public `{ error: { code,
@@ -81,7 +93,8 @@ export function createErrorNormalizationMiddleware(deps: {
81
93
  gateway?.status ?? builtinStatus ?? (isBuiltinError || isInputError ? 400 : 500);
82
94
  const code =
83
95
  gateway?.code ?? builtinCode ?? (isBuiltinError || isInputError ? "invalid_request" : "internal_error");
84
- const message =
96
+ const requestId = state?.requestId ?? crypto.randomUUID();
97
+ const rawMessage =
85
98
  gateway !== undefined
86
99
  ? explainGatewayError(gateway)
87
100
  : isInputError && error instanceof Error
@@ -90,8 +103,9 @@ export function createErrorNormalizationMiddleware(deps: {
90
103
  code,
91
104
  isBuiltinError
92
105
  ? builtin?.message ?? "Unable to process the request as written."
93
- : "The gateway hit an internal error. Retry; if it persists, the server logs carry the request id.",
106
+ : "The gateway hit an internal error. Retry; if it persists, quote the request id to support. Even we have bad days — retry, and quote the id if it persists 🐻",
94
107
  );
108
+ const message = applyRequestId(rawMessage, requestId);
95
109
  const origin = gateway?.origin ?? "cartethyia";
96
110
  // `afterResponse` telemetry hook can enqueue it even when the request
97
111
  // never reached canonical parse / auth / preparation.
@@ -108,7 +122,7 @@ export function createErrorNormalizationMiddleware(deps: {
108
122
  if (!state?.canonicalRequest && !state?.authorization)
109
123
  metrics.proxy_requests_total.inc(1, { status: "rejected" });
110
124
  set.headers["cache-control"] = "no-store";
111
- set.headers["x-request-id"] = state?.requestId ?? crypto.randomUUID();
125
+ set.headers["x-request-id"] = requestId;
112
126
  Object.assign(set.headers, GATEWAY_SECURITY_HEADERS);
113
127
  if (deps.hsts) set.headers["strict-transport-security"] = "max-age=31536000";
114
128
  set.status = status;
@@ -128,6 +142,7 @@ export function createErrorNormalizationMiddleware(deps: {
128
142
  error: {
129
143
  origin,
130
144
  code,
145
+ requestId,
131
146
  message,
132
147
  ...(gateway
133
148
  ? { details: publicGatewayErrorDetails(gateway) }
@@ -8,15 +8,19 @@ import { GatewayError } from "../../protocol/gateway-error";
8
8
 
9
9
  /** Thrown when the request's client peer address cannot be established. */
10
10
  export function peerAddressUnavailableError(): GatewayError {
11
- return new GatewayError("admission_unavailable", 503, "client peer address unavailable");
11
+ return new GatewayError(
12
+ "admission_unavailable",
13
+ 503,
14
+ "client peer address unavailable (request {requestId}). We couldn't see who's knocking — retry 🚪",
15
+ );
12
16
  }
13
17
 
14
18
  /** Thrown when the request body exceeds the endpoint's byte budget. */
15
19
  export function bodyTooLargeError(maxBytes: number): GatewayError {
16
20
  return new GatewayError(
17
- "invalid_request",
21
+ "request_too_large",
18
22
  413,
19
- `The request body is larger than this endpoint accepts (${maxBytes} bytes). Shorten the prompt or split the request.`,
23
+ `The request body is larger than this endpoint accepts (${maxBytes} bytes) (request {requestId}). Shorten the prompt or split the request. We love long reads, but not that long 📚`,
20
24
  );
21
25
  }
22
26
 
@@ -90,7 +90,7 @@ export function createHeadroomGuard(deps: HeadroomGuardDeps): Elysia {
90
90
  throw new GatewayError(
91
91
  "overloaded",
92
92
  503,
93
- "The gateway is at capacity; retry shortly.",
93
+ "The gateway is at capacity (request {requestId}); retry shortly. It's not you, it's us — come back in a few minutes ☕️",
94
94
  {
95
95
  retryAfterMs: SHED_RETRY_MS,
96
96
  minFreeBytes: headroom,
@@ -1,4 +1,4 @@
1
- import { Elysia } from "elysia";
1
+ import { Elysia, type AnyElysia } from "elysia";
2
2
  import { createIngressPolicyMiddleware } from "./body-policy";
3
3
  import {
4
4
  createCanonicalRequestMiddleware,
@@ -81,9 +81,9 @@ export interface TransportPipelineContext {
81
81
  */
82
82
  export interface TransportPipeline {
83
83
  /** Applies root-scoped request context and error normalization. */
84
- mountRoot(app: Elysia<any, any, any, any, any, any, any, any>): void;
84
+ mountRoot(app: AnyElysia): void;
85
85
  /** Builds the complete `/v1/*` gateway plugin around registered routes. */
86
- createGateway(registerRoutes: (routes: Elysia<any, any, any, any, any, any, any, any>) => void): Elysia;
86
+ createGateway(registerRoutes: (routes: AnyElysia) => void): Elysia;
87
87
  }
88
88
 
89
89
  /**
@@ -129,7 +129,7 @@ export function createTransportPipeline(context: TransportPipelineContext): Tran
129
129
  preparer: context.preparer,
130
130
  }),
131
131
  ];
132
- const mountRoot = (app: Elysia<any, any, any, any, any, any, any, any>): void => {
132
+ const mountRoot = (app: AnyElysia): void => {
133
133
  app.use(
134
134
  createRequestContextMiddleware({
135
135
  stateStore: context.stateStore,
@@ -205,8 +205,8 @@ export function createTransportPipeline(context: TransportPipelineContext): Tran
205
205
  }
206
206
  };
207
207
  const createGateway = (
208
- registerRoutes: (routes: Elysia<any, any, any, any, any, any, any, any>) => void,
209
- ): Elysia<any, any, any, any, any, any, any, any> => {
208
+ registerRoutes: (routes: AnyElysia) => void,
209
+ ): AnyElysia => {
210
210
  const gateway = new Elysia({ prefix: "/v1" });
211
211
  for (const plugin of stages) gateway.use(plugin);
212
212
  // No telemetry/cleanup registration here: the lifecycle is owned by
@@ -21,8 +21,22 @@ export function createDependencyReadinessMiddleware(deps: ReadinessMiddlewareDep
21
21
  throw new GatewayError(notice.code, 503, notice.message);
22
22
  }
23
23
  const readiness = await deps.readiness();
24
- if (readiness.status !== "ready")
25
- throw new GatewayError("platform_unavailable", 503, "The gateway's dependencies are not ready, so no request can be served. Retry shortly; if it persists, check the database and coordination backend.");
24
+ if (readiness.status !== "ready") {
25
+ // A probe that timed out under load says nothing about the dependencies —
26
+ // it could not get a backend in time, usually because the process is
27
+ // saturated. Surface that as overload, not as a broken dependency. A
28
+ // hard failure (connection refused, migrations missing) keeps the
29
+ // dependency message, which tells the operator what to actually fix.
30
+ if (readiness.reason?.endsWith("timed out")) {
31
+ throw new GatewayError(
32
+ "overloaded",
33
+ 503,
34
+ "The gateway is overloaded (request {requestId}); retry shortly. It's not you, it's us — back in a moment ☕️",
35
+ { retryAfterMs: 1_000 },
36
+ );
37
+ }
38
+ throw new GatewayError("platform_unavailable", 503, "The gateway's dependencies are not ready, so no request can be served (request {requestId}). Retry shortly; if it persists, check the database and coordination backend. Warming up — back in a moment ☕️");
39
+ }
26
40
  })
27
41
  .as("plugin");
28
42
  }
@@ -159,7 +159,7 @@ export function createCanonicalRequestMiddleware(deps: {
159
159
  // literal `null` body) is a valid single-read value and flows to the
160
160
  // surface adapter's parse error rather than triggering a second read.
161
161
  if (state.ingressBody === undefined)
162
- throw new GatewayError("admission_unavailable", 503, "The request body was never read, so this route cannot be served. Resend the request.");
162
+ throw new GatewayError("route_unavailable", 503, "The request body was never read, so this route cannot be served (request {requestId}). Resend the request. The bouncer stepped out — resend shortly 🚪");
163
163
  const body = state.ingressBody;
164
164
  const headers: Record<string, string> = {};
165
165
  request.headers.forEach((value, key) => {
@@ -192,7 +192,7 @@ export function createCanonicalRequestMiddleware(deps: {
192
192
  throw new GatewayError(
193
193
  "capability_unsupported",
194
194
  400,
195
- `Surface adapter unavailable: ${detection.surface}`,
195
+ `Surface adapter unavailable: ${detection.surface} (request {requestId}). Know your tools — this one just isn't in the box 🧰`,
196
196
  { surface: detection.surface },
197
197
  );
198
198
  const ingress = deps.stateStore.require(request);
@@ -224,7 +224,7 @@ export function createProxyRoutePreparationMiddleware(deps: {
224
224
  if (!isJsonRoute || path === "/v1/responses/compact" || isOpaqueBodyRoute(path)) return;
225
225
  const state = deps.stateStore.require(request);
226
226
  if (!state.authorization || !state.canonicalRequest)
227
- throw new GatewayError("admission_unavailable", 503, "The request was not authenticated and parsed, so this route cannot be served. Resend the request.");
227
+ throw new GatewayError("route_unavailable", 503, "The request was not authenticated and parsed, so this route cannot be served (request {requestId}). Resend the request. The bouncer stepped out — resend shortly 🚪");
228
228
  // The key rides along so a strike is charged to the caller that made it.
229
229
  // Without it, one recipient of a shared link could escalate a ban onto
230
230
  try {
@@ -101,7 +101,7 @@ export function resolveContinuationAffinity(
101
101
  throw new GatewayError(
102
102
  "invalid_request",
103
103
  400,
104
- "These tool results belong to different Cursor accounts; resend them one account at a time.",
104
+ "These tool results belong to different Cursor accounts (request {requestId}); resend them one account at a time. The session went cold — reconnect or resend 🔌",
105
105
  { callIds: [...ids] },
106
106
  );
107
107
  }
@@ -189,9 +189,9 @@ export class ProxyRequestPreparer {
189
189
  }): Promise<PreparedProxyRequest> {
190
190
  const { canonicalRequest: initialRequest, authorization, signal } = input;
191
191
  if (signal?.aborted)
192
- throw new GatewayError("transport_closed", 499, "request was cancelled");
192
+ throw new GatewayError("transport_closed", 499, "The request was cancelled (request {requestId}). Send it again whenever ready 🙃");
193
193
  if (!initialRequest.model || initialRequest.model.trim().length === 0) {
194
- throw new GatewayError("invalid_request", 400, "Request requires a non-empty model identifier", {
194
+ throw new GatewayError("invalid_request", 400, "Request requires a non-empty model identifier (request {requestId}). Check the shape of what you sent — then resend 🪞", {
195
195
  field: "model",
196
196
  });
197
197
  }
@@ -242,13 +242,13 @@ export class ProxyRequestPreparer {
242
242
  !allowedForKey
243
243
  )
244
244
  throw new GatewayError(
245
- "model_not_found",
246
- 404,
247
- `This key may only use models starting with '${authorization.modelPrefix}'. Ask for one inside that prefix, or have an operator widen the key.`,
245
+ "model_not_allowed",
246
+ 403,
247
+ `This key may only use models starting with '${authorization.modelPrefix}' (request {requestId}). Ask for one inside that prefix, or have an operator widen the key. The door's locked for this one — check the allowlist 🔒`,
248
248
  { model: request.model, required_prefix: authorization.modelPrefix },
249
249
  );
250
250
  if (!allowedForKey) {
251
- throw new GatewayError("model_not_found", 404, "model is not allowed for this API key", {
251
+ throw new GatewayError("model_not_allowed", 403, "model is not allowed for this API key (request {requestId}). The door's locked for this one — check the allowlist 🔒", {
252
252
  model: request.model,
253
253
  });
254
254
  }
@@ -307,7 +307,7 @@ export class ProxyRequestPreparer {
307
307
  throw new GatewayError(
308
308
  "capability_unsupported",
309
309
  400,
310
- "no eligible route supports this request's capabilities",
310
+ "no eligible route supports this request's capabilities (request {requestId}). Drop the unsupported capability, or route to a model that accepts it. Know your tools — this one just isn't in the box 🧰",
311
311
  { model: request.model },
312
312
  );
313
313
  const policyCandidates = plan.candidates.filter((candidate) =>
@@ -319,7 +319,7 @@ export class ProxyRequestPreparer {
319
319
  ),
320
320
  );
321
321
  if (policyCandidates.length === 0)
322
- throw new GatewayError("model_not_found", 404, "model is not allowed for this API key", {
322
+ throw new GatewayError("model_not_allowed", 403, "model is not allowed for this API key (request {requestId}). The door's locked for this one — check the allowlist 🔒", {
323
323
  model: request.model,
324
324
  });
325
325
  if (policyCandidates.length !== plan.candidates.length)
@@ -341,7 +341,7 @@ export class ProxyRequestPreparer {
341
341
  throw new GatewayError(
342
342
  "capability_unsupported",
343
343
  400,
344
- `this model is not served on a chat wire — it is a '${kind}' service model${route === undefined ? "" : `; call it at POST ${route}`}`,
344
+ `this model is not served on a chat wire — it is a '${kind}' service model${route === undefined ? "" : `; call it at POST ${route}`} (request {requestId}). Know your tools — this one just isn't in the box 🧰`,
345
345
  { model: request.model, service_kind: kind, ...(route === undefined ? {} : { route }) },
346
346
  );
347
347
  }
@@ -366,7 +366,7 @@ export class ProxyRequestPreparer {
366
366
  }
367
367
  }
368
368
  if (signal?.aborted)
369
- throw new GatewayError("transport_closed", 499, "request was cancelled");
369
+ throw new GatewayError("transport_closed", 499, "The request was cancelled (request {requestId}). Send it again whenever ready 🙃");
370
370
  const eligible = plan.candidates;
371
371
  // The buddy gateway rejects partial tool rounds outright (`11148`) where
372
372
  // the generic synthesis policy would insert a placeholder result, so a
@@ -401,7 +401,7 @@ export class ProxyRequestPreparer {
401
401
  effectiveRequest = sanitizeRequestToolIds(effectiveRequest);
402
402
  }
403
403
  if (signal?.aborted)
404
- throw new GatewayError("transport_closed", 499, "request was cancelled");
404
+ throw new GatewayError("transport_closed", 499, "The request was cancelled (request {requestId}). Send it again whenever ready 🙃");
405
405
  // Project against the chosen candidate, not the intersection of every
406
406
  // candidate in the plan. Still required for generation-control stripping
407
407
  // even on the no-tools path — keep it, but it is cheap vs message clones.
@@ -448,7 +448,7 @@ export class ProxyRequestPreparer {
448
448
  readonly emptyMessage: string;
449
449
  }): Promise<PreparedNativeRequest> {
450
450
  if (input.signal?.aborted)
451
- throw new GatewayError("transport_closed", 499, "request was cancelled");
451
+ throw new GatewayError("transport_closed", 499, "The request was cancelled (request {requestId}). Send it again whenever ready 🙃");
452
452
  const snapshot = await this.deps.snapshotService.getSnapshot();
453
453
  const allowCliMappings = allowsCliToolMappings(input.authorization.scopes, {
454
454
  ...(input.clientUserAgent === undefined ? {} : { userAgent: input.clientUserAgent }),
@@ -473,13 +473,13 @@ export class ProxyRequestPreparer {
473
473
  !allowedForKey
474
474
  )
475
475
  throw new GatewayError(
476
- "model_not_found",
477
- 404,
478
- `This key may only use models starting with '${input.authorization.modelPrefix}'. Ask for one inside that prefix, or have an operator widen the key.`,
476
+ "model_not_allowed",
477
+ 403,
478
+ `This key may only use models starting with '${input.authorization.modelPrefix}' (request {requestId}). Ask for one inside that prefix, or have an operator widen the key. The door's locked for this one — check the allowlist 🔒`,
479
479
  { model: input.model, required_prefix: input.authorization.modelPrefix },
480
480
  );
481
481
  if (!allowedForKey)
482
- throw new GatewayError("model_not_found", 404, "model is not allowed for this API key", {
482
+ throw new GatewayError("model_not_allowed", 403, "model is not allowed for this API key (request {requestId}). The door's locked for this one — check the allowlist 🔒", {
483
483
  model: input.model,
484
484
  });
485
485
  const plan = await this.deps.routingEngine.plan(
@@ -491,7 +491,7 @@ export class ProxyRequestPreparer {
491
491
  input.authorization.cliMappingOwnerId ?? input.authorization.id,
492
492
  );
493
493
  if (input.signal?.aborted)
494
- throw new GatewayError("transport_closed", 499, "request was cancelled");
494
+ throw new GatewayError("transport_closed", 499, "The request was cancelled (request {requestId}). Send it again whenever ready 🙃");
495
495
  const policyCandidates = plan.candidates.filter((candidate) =>
496
496
  isModelAllowed(
497
497
  input.authorization.snapshot,
@@ -501,12 +501,12 @@ export class ProxyRequestPreparer {
501
501
  ),
502
502
  );
503
503
  if (policyCandidates.length === 0)
504
- throw new GatewayError("model_not_found", 404, "model is not allowed for this API key", {
504
+ throw new GatewayError("model_not_allowed", 403, "model is not allowed for this API key (request {requestId}). The door's locked for this one — check the allowlist 🔒", {
505
505
  model: input.model,
506
506
  });
507
507
  const candidates = policyCandidates.filter(input.eligible);
508
508
  if (candidates.length === 0)
509
- throw new GatewayError("capability_unsupported", 400, input.emptyMessage, {
509
+ throw new GatewayError("capability_unsupported", 400, `${input.emptyMessage} (request {requestId}). Know your tools — this one just isn't in the box 🧰`, {
510
510
  model: input.model,
511
511
  });
512
512
  return {
@@ -191,15 +191,12 @@ function subscribeInFlightSnapshot(
191
191
  store: { inFlightSnapshot(): InFlightSnapshot },
192
192
  listener: (snapshot: InFlightSnapshot) => void,
193
193
  ): () => void {
194
- const timers = new Set<ReturnType<typeof setTimeout>>();
195
194
  const push = (): void => listener(store.inFlightSnapshot());
196
195
  push();
197
196
  const handle = setInterval(push, 1_000);
198
197
  (handle as unknown as { unref?: () => void })?.unref?.();
199
198
  return () => {
200
199
  clearInterval(handle);
201
- for (const t of timers) clearTimeout(t);
202
- timers.clear();
203
200
  };
204
201
  }
205
202
 
@@ -259,7 +256,7 @@ export class ProxyRequestStateStore {
259
256
  try {
260
257
  if (controller && !controller.signal.aborted) {
261
258
  controller.abort(
262
- new GatewayError("deadline_exceeded", 504, "in-flight request exceeded its deadline"),
259
+ new GatewayError("deadline_exceeded", 504, "in-flight request exceeded its deadline (request {requestId}). Some things just take longer 🐢"),
263
260
  );
264
261
  }
265
262
  } catch {
@@ -292,7 +289,7 @@ export class ProxyRequestStateStore {
292
289
  const abortReason =
293
290
  reason instanceof Error || reason instanceof DOMException
294
291
  ? reason
295
- : new GatewayError("shutting_down", 503, "The gateway is shutting down, so this request cannot be served. Retry against a live instance.");
292
+ : new GatewayError("shutting_down", 503, "The gateway is shutting down, so this request cannot be served (request {requestId}). Retry against a live instance. Maintenance mode — back in a minute 🧘");
296
293
  for (const controller of controllers) {
297
294
  try {
298
295
  if (!controller.signal.aborted) controller.abort(abortReason);
@@ -330,7 +327,7 @@ export class ProxyRequestStateStore {
330
327
  new GatewayError(
331
328
  "deadline_exceeded",
332
329
  504,
333
- "request deadline exceeded",
330
+ "request deadline exceeded (request {requestId}). Some things just take longer 🐢",
334
331
  {},
335
332
  "cartethyia",
336
333
  ),
@@ -418,7 +415,7 @@ export class ProxyRequestStateStore {
418
415
  require(request: Request): ProxyRequestState {
419
416
  const state = this.states.get(request);
420
417
  if (!state)
421
- throw new GatewayError("admission_unavailable", 503, "The request was never registered, so it cannot be served. Resend the request.");
418
+ throw new GatewayError("route_unavailable", 503, "The request was never registered, so it cannot be served (request {requestId}). Resend the request. The bouncer stepped out — resend shortly 🚪");
422
419
  return state;
423
420
  }
424
421
 
@@ -10,6 +10,7 @@ import {
10
10
  networkPools,
11
11
  poolRoutingSettings,
12
12
  providerAccounts,
13
+ providerPoolAccounts,
13
14
  providerRoutingSettings,
14
15
  providers,
15
16
  tenantDisabledModels,
@@ -372,6 +373,17 @@ const POOL_COLUMNS = {
372
373
  weight: networkPools.weight,
373
374
  } as const;
374
375
 
376
+ const POOL_ACCOUNT_COLUMNS = {
377
+ id: providerPoolAccounts.id,
378
+ tenantId: providerPoolAccounts.tenantId,
379
+ networkPoolId: providerPoolAccounts.networkPoolId,
380
+ providerId: providerPoolAccounts.providerId,
381
+ label: providerPoolAccounts.label,
382
+ status: providerPoolAccounts.status,
383
+ cooldownUntil: providerPoolAccounts.cooldownUntil,
384
+ lastErrorCategory: providerPoolAccounts.lastErrorCategory,
385
+ } as const;
386
+
375
387
  const DISABLED_MODEL_COLUMNS = {
376
388
  tenantId: tenantDisabledModels.tenantId,
377
389
  providerId: tenantDisabledModels.providerId,
@@ -414,7 +426,7 @@ class RouteCatalogRepository {
414
426
  constructor(private readonly db: CartethyiaDatabase) {}
415
427
 
416
428
  async loadRouteCatalogSnapshot(tenantId?: string): Promise<RouteCatalogSnapshotResult> {
417
- const [providerRows, modelRows, accountRows, aliasRows, comboRows, routingRows, poolRows, disabledModelRows, cliMappingRows, cliMappingSettingRows, poolSettingRows, settingsRows] =
429
+ const [providerRows, modelRows, accountRows, aliasRows, comboRows, routingRows, poolRows, poolAccountRows, disabledModelRows, cliMappingRows, cliMappingSettingRows, poolSettingRows, settingsRows] =
418
430
  await Promise.all([
419
431
  this.db.select(PROVIDER_COLUMNS).from(providers).where(eq(providers.enabled, true)),
420
432
  this.db.select(MODEL_COLUMNS).from(models),
@@ -427,6 +439,7 @@ class RouteCatalogRepository {
427
439
  this.db.select(COMBO_COLUMNS).from(modelCombos),
428
440
  this.db.select(ROUTING_COLUMNS).from(providerRoutingSettings),
429
441
  this.db.select(POOL_COLUMNS).from(networkPools),
442
+ this.db.select(POOL_ACCOUNT_COLUMNS).from(providerPoolAccounts),
430
443
  this.db.select(DISABLED_MODEL_COLUMNS).from(tenantDisabledModels),
431
444
  this.db.select(CLI_MAPPING_COLUMNS).from(cliToolMappings),
432
445
  this.db.select(CLI_MAPPING_SETTING_COLUMNS).from(cliToolSettings),
@@ -500,6 +513,28 @@ class RouteCatalogRepository {
500
513
  activePoolsByTenant.set(pool.tenantId, list);
501
514
  }
502
515
 
516
+ // Pool-as-account rows, grouped by tenant: one per (pool × ip-scoped
517
+ // provider). For a credential-less ip-scoped provider these are the
518
+ // account-equivalent routing entities — a spent egress is excluded from
519
+ // the plan exactly like a cooling account, and the candidate carries the
520
+ // pool-account's health (see the public-candidate fan-out below).
521
+ const poolAccountsByTenant = new Map<
522
+ string,
523
+ Array<{
524
+ id: string;
525
+ networkPoolId: string;
526
+ providerId: string;
527
+ status: string;
528
+ cooldownUntil: Date | null;
529
+ lastErrorCategory: string | null;
530
+ }>
531
+ >();
532
+ for (const poolAccount of poolAccountRows) {
533
+ const list = poolAccountsByTenant.get(poolAccount.tenantId) ?? [];
534
+ list.push(poolAccount);
535
+ poolAccountsByTenant.set(poolAccount.tenantId, list);
536
+ }
537
+
503
538
  // Per-tenant pool selection strategy; an absent row reads as the
504
539
  // `least_loaded` default at selection time.
505
540
  const poolRouting: Record<string, PoolRoutingSetting> = {};
@@ -657,7 +692,67 @@ class RouteCatalogRepository {
657
692
  if (accounts.length === 0) {
658
693
  const providerRequiresAccount = requiresAccountById.get(model.providerId) ?? true;
659
694
  if (!providerRequiresAccount) {
660
- // Public models inherit active pools for each tenant that has active pools
695
+ // Public models inherit active pools for each tenant that has active pools.
696
+ // When the provider is ip-scoped (limits follow the egress address), the
697
+ // tenant's pool-account rows are the account-equivalent routing entities:
698
+ // one candidate per pool, carrying that pool-account's health, so a spent
699
+ // egress is excluded from the plan exactly like a cooling account.
700
+ const ownedTenantIds = [...poolAccountsByTenant.keys()].filter((tid) =>
701
+ (poolAccountsByTenant.get(tid) ?? []).some((pa) => pa.providerId === model.providerId),
702
+ );
703
+ let emittedPoolAccount = false;
704
+ for (const tenantOwnerId of ownedTenantIds) {
705
+ const poolAccounts = poolAccountsByTenant.get(tenantOwnerId) ?? [];
706
+ const tenantPoolAccounts = new Map<string, typeof poolAccounts>();
707
+ for (const pa of poolAccounts) {
708
+ if (pa.providerId !== model.providerId) continue;
709
+ const list = tenantPoolAccounts.get(pa.networkPoolId) ?? [];
710
+ list.push(pa);
711
+ tenantPoolAccounts.set(pa.networkPoolId, list);
712
+ }
713
+ if (tenantPoolAccounts.size === 0) continue;
714
+ for (const [poolId, pas] of tenantPoolAccounts) {
715
+ const first = pas[0];
716
+ if (!first) continue;
717
+ const tenantPools = resolveNetworkPools(model.providerId, tenantOwnerId);
718
+ if (!tenantPools) continue;
719
+ emittedPoolAccount = true;
720
+ const tenantCandidate: RouteCandidateWithHealth = {
721
+ provider_id: model.providerId,
722
+ model_id: model.modelId,
723
+ wire_family: model.wireFamily as WireFamily,
724
+ service_kind: model.serviceKind,
725
+ endpoint: model.endpointPath,
726
+ ...(model.outputLimit == null ? {} : { output_limit: model.outputLimit }),
727
+ capability_profile: capabilityProfile,
728
+ tenant_id: tenantOwnerId,
729
+ // Public credential-less candidates have no account to own them.
730
+ owner_tenant_id: null,
731
+ requires_account: false as const,
732
+ network_pool_ids: [poolId],
733
+ network_pool_required: true,
734
+ network_pool_limits: tenantPools.limits,
735
+ network_pool_weights: tenantPools.weights,
736
+ ...(tenantPools.routing ? { network_pool_routing: tenantPools.routing } : {}),
737
+ ...providerLimitFor(model.providerId, tenantOwnerId),
738
+ ...(first.status === "disabled"
739
+ ? { health_status: "disabled" as const }
740
+ : first.status === "cooldown"
741
+ ? {
742
+ health_status: "cooldown" as const,
743
+ cooldown_kind: hardCooldownCategory(first.lastErrorCategory) ? "hard" : "soft",
744
+ ...(first.cooldownUntil && first.cooldownUntil.getTime() > Date.now()
745
+ ? { cooldown_until: first.cooldownUntil.getTime() }
746
+ : {}),
747
+ }
748
+ : {}),
749
+ };
750
+ if (tenantId === undefined || tenantId === tenantOwnerId) {
751
+ candidates.push(tenantCandidate);
752
+ }
753
+ }
754
+ }
755
+ if (emittedPoolAccount) continue;
661
756
  for (const tId of configuredPoolTenants) {
662
757
  const tenantPools = resolveNetworkPools(model.providerId, tId);
663
758
  if (tenantPools) {
@@ -358,7 +358,7 @@ export function ambiguousModelError(bare: string, owners: string[]): GatewayErro
358
358
  return new GatewayError(
359
359
  "ambiguous_model",
360
360
  400,
361
- `'${bare}' matches more than one provider (${owners.join(", ")}). Qualify it as '${owners[0]}/${bare}', or add an alias to pick one.`,
361
+ `'${bare}' matches more than one provider (${owners.join(", ")}). Qualify it as '${owners[0]}/${bare}', or add an alias to pick one. Too popular for its own good — qualify it 🧩`,
362
362
  {
363
363
  model: bare,
364
364
  owners,
@@ -380,7 +380,7 @@ export function modelNotFoundError(requested: string, resolved: readonly string[
380
380
  404,
381
381
  `'${requested}' is not routed${
382
382
  targets.length > 0 ? ` — it resolved to ${hint}, which no account serves` : ""
383
- }. Enable the model on an account, or point the alias at one that exists.`,
383
+ } (request {requestId}). Enable the model on an account, or point the alias at one that exists. Try another model — this one's off the menu 📚`,
384
384
  {
385
385
  model: requested,
386
386
  ...(targets.length > 0 ? { resolved_models: targets } : {}),
@@ -392,7 +392,7 @@ export function capabilityUnsupportedError(capability: string): GatewayError {
392
392
  return new GatewayError(
393
393
  "capability_unsupported",
394
394
  400,
395
- `No account serving this model supports ${capability}. Drop the ${capability} part of the request, or route to a model that accepts it.`,
395
+ `No account serving this model supports ${capability} (request {requestId}). Drop the ${capability} part of the request, or route to a model that accepts it. Know your tools — this one just isn't in the box 🧰`,
396
396
  { capability },
397
397
  );
398
398
  }
@@ -424,8 +424,8 @@ export function capacityExhaustedError(context?: {
424
424
  "provider_capacity_exhausted",
425
425
  429,
426
426
  onlyOne
427
- ? "The only account routing this model is at its concurrency limit. Retry shortly; if it persists, raise the provider's max-inflight or add another account."
428
- : "Every account is at its concurrency limit. Retry shortly; if it persists, raise the provider's max-inflight or add an account.",
427
+ ? "The only account routing this model is at its concurrency limit (request {requestId}). Retry shortly; if it persists, raise the provider's max-inflight or add another account. Even the machine needs a rest — chill 🦋"
428
+ : "Every account is at its concurrency limit (request {requestId}). Retry shortly; if it persists, raise the provider's max-inflight or add an account. Even the machine needs a rest — chill 🦋",
429
429
  {
430
430
  ...(context?.providerId !== undefined ? { providerId: context.providerId } : {}),
431
431
  ...(context?.model !== undefined ? { model: context.model } : {}),
@@ -456,7 +456,7 @@ export function accountsUnavailableError(
456
456
  return new GatewayError(
457
457
  "accounts_unavailable",
458
458
  503,
459
- `No account in the pool can serve '${requested}' right now (${explainEligibilityReasons(distinct)}). Add an account, or wait for an operator to re-enable one.`,
459
+ `No account in the pool can serve '${requested}' right now (request {requestId}) (${explainEligibilityReasons(distinct)}). Add an account, or wait for an operator to re-enable one. Every account is on a coffee break — try later ☕️`,
460
460
  { model: requested, reasons: distinct, candidate_count: reasons.length, gateway_hint: "no_healthy_account" },
461
461
  );
462
462
  }
@@ -492,7 +492,7 @@ export function accountsRateLimitedError(
492
492
  return new GatewayError(
493
493
  "accounts_rate_limited",
494
494
  429,
495
- `Every account serving '${requested}'${routeNote} is cooling down after hitting upstream limits. The request is fine — retry once the cooldown resets.`,
495
+ `Every account serving '${requested}'${routeNote} is cooling down after hitting upstream limits (request {requestId}). The request is fine — retry once the cooldown resets. They're resting up 🌸`,
496
496
  {
497
497
  model: requested,
498
498
  routed_model: routed,
@@ -624,7 +624,7 @@ export class RoutingEngine {
624
624
  throw new GatewayError(
625
625
  "invalid_request",
626
626
  400,
627
- "This tool result belongs to a Cursor account that is no longer routable. Reconnect the account, or resend the complete conversation.",
627
+ "This tool result belongs to a Cursor account that is no longer routable (request {requestId}). Reconnect the account, or resend the complete conversation. The session went cold — reconnect or resend 🔌",
628
628
  {
629
629
  providerId: affinity.providerId,
630
630
  accountId: affinity.providerAccountId,
@@ -652,7 +652,7 @@ export class RoutingEngine {
652
652
  throw new GatewayError(
653
653
  "invalid_request",
654
654
  400,
655
- "This tool result belongs to an account that is no longer routable. Reconnect the account, or resend the complete conversation.",
655
+ "This tool result belongs to an account that is no longer routable (request {requestId}). Reconnect the account, or resend the complete conversation. The session went cold — reconnect or resend 🔌",
656
656
  {
657
657
  providerId: affinity.providerId,
658
658
  accountId: affinity.providerAccountId,
@@ -16,8 +16,9 @@
16
16
  import { GatewayError, type GatewayErrorCode } from "../protocol/gateway-error";
17
17
 
18
18
  export interface ShutdownNotice {
19
- readonly code: Extract<GatewayErrorCode, "shutting_down" | "restart_for_update">;
19
+ readonly code: Extract<GatewayErrorCode, "shutting_down">;
20
20
  readonly message: string;
21
+ readonly reason?: string;
21
22
  }
22
23
 
23
24
  /**
@@ -30,9 +31,17 @@ export const UPDATE_SHUTDOWN_REASON = "update";
30
31
 
31
32
  export function shutdownNotice(reason: string | undefined): ShutdownNotice {
32
33
  if (reason === UPDATE_SHUTDOWN_REASON) {
33
- return { code: "restart_for_update", message: "system will be back in a minute" };
34
+ return {
35
+ code: "shutting_down",
36
+ message:
37
+ "Service is shutting down for an update; it will be back in about a minute (request {requestId}). Maintenance mode — back in a minute 🧘",
38
+ reason: "restart_for_update",
39
+ };
34
40
  }
35
- return { code: "shutting_down", message: "Service is shutting down" };
41
+ return {
42
+ code: "shutting_down",
43
+ message: "Service is shutting down (request {requestId}). Come back shortly 🧘",
44
+ };
36
45
  }
37
46
 
38
47
  /**
@@ -47,7 +56,12 @@ export function shutdownNotice(reason: string | undefined): ShutdownNotice {
47
56
  */
48
57
  export function shutdownError(reason: string | undefined): GatewayError {
49
58
  const notice = shutdownNotice(reason);
50
- return new GatewayError(notice.code, 503, notice.message);
59
+ return new GatewayError(
60
+ notice.code,
61
+ 503,
62
+ notice.message,
63
+ notice.reason === undefined ? {} : { reason: notice.reason },
64
+ );
51
65
  }
52
66
 
53
67
  /**
@@ -62,7 +76,5 @@ export function shutdownError(reason: string | undefined): GatewayError {
62
76
  */
63
77
  export function drainAbortReason(reason: unknown): GatewayError | undefined {
64
78
  if (!(reason instanceof GatewayError)) return undefined;
65
- return reason.code === "shutting_down" || reason.code === "restart_for_update"
66
- ? reason
67
- : undefined;
79
+ return reason.code === "shutting_down" ? reason : undefined;
68
80
  }
@@ -166,7 +166,7 @@ export class CompletionAdapter implements SurfaceAdapter {
166
166
  throw new GatewayError(
167
167
  "invalid_request",
168
168
  400,
169
- "completion requests require a non-empty prompt",
169
+ "completion requests require a non-empty prompt (request {requestId}). Check the shape of what you sent — then resend 🪞",
170
170
  );
171
171
  const controls: GenerationControls = {};
172
172
  for (const field of ["temperature", "top_p", "n", "seed"] as const)