cartethyia 2.7.2 → 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 (405) hide show
  1. package/.env.example +2 -0
  2. package/dist/dashboard/assets/{BannedPage-XaUmiBrC.js → BannedPage-ChG5svFb.js} +1 -1
  3. package/dist/dashboard/assets/BannedPage-ChG5svFb.js.gz +0 -0
  4. package/dist/dashboard/assets/CliToolDetailPage-BIcWQun7.js +1 -0
  5. package/dist/dashboard/assets/CliToolDetailPage-BIcWQun7.js.gz +0 -0
  6. package/dist/dashboard/assets/CliToolsPage-Bf2VelDe.js +1 -0
  7. package/dist/dashboard/assets/CliToolsPage-Bf2VelDe.js.gz +0 -0
  8. package/dist/dashboard/assets/{CombosPage-CXGS_Ct-.js → CombosPage-DCgm5g6g.js} +1 -1
  9. package/dist/dashboard/assets/CombosPage-DCgm5g6g.js.gz +0 -0
  10. package/dist/dashboard/assets/ConsoleLogPage-a8mD0QF1.js +1 -0
  11. package/dist/dashboard/assets/ConsoleLogPage-a8mD0QF1.js.gz +0 -0
  12. package/dist/dashboard/assets/CustomizationPage-ggeMSjte.js +1 -0
  13. package/dist/dashboard/assets/CustomizationPage-ggeMSjte.js.gz +0 -0
  14. package/dist/dashboard/assets/{HealthEventsModal-BKtAPYVF.js → HealthEventsModal-DzSWzE_j.js} +1 -1
  15. package/dist/dashboard/assets/HealthEventsModal-DzSWzE_j.js.gz +0 -0
  16. package/dist/dashboard/assets/{LoginPage-BdmRLnq2.js → LoginPage-U6AKaJcF.js} +1 -1
  17. package/dist/dashboard/assets/LoginPage-U6AKaJcF.js.gz +0 -0
  18. package/dist/dashboard/assets/{ModelPicker-BH8Tehdi.js → ModelPicker-BMS2t8BA.js} +1 -1
  19. package/dist/dashboard/assets/ModelPicker-BMS2t8BA.js.gz +0 -0
  20. package/dist/dashboard/assets/OverviewPage-D2D4IiHl.js +1 -0
  21. package/dist/dashboard/assets/OverviewPage-D2D4IiHl.js.gz +0 -0
  22. package/dist/dashboard/assets/ProviderDetailPage-CrNf7j_X.js +5 -0
  23. package/dist/dashboard/assets/ProviderDetailPage-CrNf7j_X.js.gz +0 -0
  24. package/dist/dashboard/assets/ProvidersPage-BSDbquc1.js +1 -0
  25. package/dist/dashboard/assets/ProvidersPage-BSDbquc1.js.gz +0 -0
  26. package/dist/dashboard/assets/ProxyPage-lsrx7NYg.js +9 -0
  27. package/dist/dashboard/assets/ProxyPage-lsrx7NYg.js.gz +0 -0
  28. package/dist/dashboard/assets/QuotaPage-L0skzML8.js +1 -0
  29. package/dist/dashboard/assets/QuotaPage-L0skzML8.js.gz +0 -0
  30. package/dist/dashboard/assets/SettingsPage-CmAbjRHW.js +1 -0
  31. package/dist/dashboard/assets/SettingsPage-CmAbjRHW.js.gz +0 -0
  32. package/dist/dashboard/assets/SetupPage-f0DE1h0-.js +1 -0
  33. package/dist/dashboard/assets/SetupPage-f0DE1h0-.js.gz +0 -0
  34. package/dist/dashboard/assets/{SortableList-Dqf4zpS-.js → SortableList-ClzpwyET.js} +1 -1
  35. package/dist/dashboard/assets/SortableList-ClzpwyET.js.gz +0 -0
  36. package/dist/dashboard/assets/{StudioPage-Byj2lAEm.js → StudioPage-Cq5tW7hW.js} +3 -3
  37. package/dist/dashboard/assets/StudioPage-Cq5tW7hW.js.gz +0 -0
  38. package/dist/dashboard/assets/UsagePage-Dfc9rWQn.js +56 -0
  39. package/dist/dashboard/assets/UsagePage-Dfc9rWQn.js.gz +0 -0
  40. package/dist/dashboard/assets/api-keys-C38iXoO4.js +1 -0
  41. package/dist/dashboard/assets/api-keys-C38iXoO4.js.gz +0 -0
  42. package/dist/dashboard/assets/app-a159k0ub.css +2 -0
  43. package/dist/dashboard/assets/app-a159k0ub.css.gz +0 -0
  44. package/dist/dashboard/assets/arrow-up-59ms1iiT.js +1 -0
  45. package/dist/dashboard/assets/arrow-up-59ms1iiT.js.gz +0 -0
  46. package/dist/dashboard/assets/assert-DbRA8EG2.js +15 -0
  47. package/dist/dashboard/assets/assert-DbRA8EG2.js.gz +0 -0
  48. package/dist/dashboard/assets/{badge-DC2L43Mj.js → badge-D9CS6dZk.js} +1 -1
  49. package/dist/dashboard/assets/badge-D9CS6dZk.js.gz +0 -0
  50. package/dist/dashboard/assets/{banner-BBVcb0J3.js → banner-DympBNq4.js} +1 -1
  51. package/dist/dashboard/assets/banner-DympBNq4.js.gz +0 -0
  52. package/dist/dashboard/assets/{card-Cca1XC6G.js → card-ibRWU_uT.js} +1 -1
  53. package/dist/dashboard/assets/card-ibRWU_uT.js.gz +0 -0
  54. package/dist/dashboard/assets/cli-tools-63qBGaRw.js +1 -0
  55. package/dist/dashboard/assets/cli-tools-63qBGaRw.js.gz +0 -0
  56. package/dist/dashboard/assets/{clipboard-button-DUzEbynq.js → clipboard-button-DJm-9pz7.js} +1 -1
  57. package/dist/dashboard/assets/clipboard-button-DJm-9pz7.js.gz +0 -0
  58. package/dist/dashboard/assets/{dialog-DFvWhwTa.js → dialog-8fiE6tec.js} +1 -1
  59. package/dist/dashboard/assets/dialog-8fiE6tec.js.gz +0 -0
  60. package/dist/dashboard/assets/drawer-CqGYeOrd.js +1 -0
  61. package/dist/dashboard/assets/drawer-CqGYeOrd.js.gz +0 -0
  62. package/dist/dashboard/assets/{entry-Bqy8RFvM.js → entry-BSAsYsmV.js} +1 -1
  63. package/dist/dashboard/assets/entry-BSAsYsmV.js.gz +0 -0
  64. package/dist/dashboard/assets/entry-CiIQULZD.js +4 -0
  65. package/dist/dashboard/assets/entry-CiIQULZD.js.gz +0 -0
  66. package/dist/dashboard/assets/entry-Daz0lJ-W.js +1 -0
  67. package/dist/dashboard/assets/entry-Daz0lJ-W.js.gz +0 -0
  68. package/dist/dashboard/assets/{concurrency-BDF0ok7y.js → fingerprint-pattern-BWuHtbW4.js} +1 -1
  69. package/dist/dashboard/assets/fingerprint-pattern-BWuHtbW4.js.gz +0 -0
  70. package/dist/dashboard/assets/{formatters-CWWZ-uCZ.js → formatters-D-MTJxMr.js} +1 -1
  71. package/dist/dashboard/assets/formatters-D-MTJxMr.js.gz +0 -0
  72. package/dist/dashboard/assets/{github-badge-C9e8Ts7e.js → github-badge-CPK47xQx.js} +1 -1
  73. package/dist/dashboard/assets/github-badge-CPK47xQx.js.gz +0 -0
  74. package/dist/dashboard/assets/{index-DFVFrnWW.js → index-DBNPOfhV.js} +2 -2
  75. package/dist/dashboard/assets/index-DBNPOfhV.js.gz +0 -0
  76. package/dist/dashboard/assets/{inline-BFUcvllj.js → inline-BT0zntEr.js} +1 -1
  77. package/dist/dashboard/assets/inline-BT0zntEr.js.gz +0 -0
  78. package/dist/dashboard/assets/{input-C_ILAvBb.js → input-Da2ZViWg.js} +1 -1
  79. package/dist/dashboard/assets/input-Da2ZViWg.js.gz +0 -0
  80. package/dist/dashboard/assets/{layout-CBIyT6Rt.js → layout-BXNeLw6M.js} +1 -1
  81. package/dist/dashboard/assets/layout-BXNeLw6M.js.gz +0 -0
  82. package/dist/dashboard/assets/{list-row-ebJ_-Nsb.js → list-row-BycOrrIV.js} +1 -1
  83. package/dist/dashboard/assets/list-row-BycOrrIV.js.gz +0 -0
  84. package/dist/dashboard/assets/live-DCQNZpF7.js +1 -0
  85. package/dist/dashboard/assets/live-DCQNZpF7.js.gz +0 -0
  86. package/dist/dashboard/assets/minimize-2-BTmMTK4o.js +1 -0
  87. package/dist/dashboard/assets/minimize-2-BTmMTK4o.js.gz +0 -0
  88. package/dist/dashboard/assets/{mutation-sMD965oO.js → mutation-Rr0TIvcj.js} +1 -1
  89. package/dist/dashboard/assets/mutation-Rr0TIvcj.js.gz +0 -0
  90. package/dist/dashboard/assets/network-9Yj2ZyLo.js +1 -0
  91. package/dist/dashboard/assets/network-9Yj2ZyLo.js.gz +0 -0
  92. package/dist/dashboard/assets/{numbers-BO9dKaFr.js → numbers-CEagj6ZZ.js} +2 -2
  93. package/dist/dashboard/assets/numbers-CEagj6ZZ.js.gz +0 -0
  94. package/dist/dashboard/assets/{query-keys-CfL_Zdoh.js → query-keys-BloKtaXJ.js} +1 -1
  95. package/dist/dashboard/assets/query-keys-BloKtaXJ.js.gz +0 -0
  96. package/dist/dashboard/assets/quota-Caw7wqci.js +1 -0
  97. package/dist/dashboard/assets/quota-Caw7wqci.js.gz +0 -0
  98. package/dist/dashboard/assets/routing-CO9zCUus.js +1 -0
  99. package/dist/dashboard/assets/routing-CO9zCUus.js.gz +0 -0
  100. package/dist/dashboard/assets/{select-DBNVLdUu.js → select-BIYIBkMU.js} +2 -2
  101. package/dist/dashboard/assets/select-BIYIBkMU.js.gz +0 -0
  102. package/dist/dashboard/assets/settings-2-Ds9DQp0t.js +1 -0
  103. package/dist/dashboard/assets/settings-2-Ds9DQp0t.js.gz +0 -0
  104. package/dist/dashboard/assets/settings-jS3-PNSA.js +1 -0
  105. package/dist/dashboard/assets/settings-jS3-PNSA.js.gz +0 -0
  106. package/dist/dashboard/assets/{stack-EMrOMt4I.js → stack-DfgnFDWY.js} +1 -1
  107. package/dist/dashboard/assets/stack-DfgnFDWY.js.gz +0 -0
  108. package/dist/dashboard/assets/{state-CWoo7ty5.js → state-CkfdYWel.js} +1 -1
  109. package/dist/dashboard/assets/state-CkfdYWel.js.gz +0 -0
  110. package/dist/dashboard/assets/{switch-CCjT6IkF.js → switch-SYm237HG.js} +1 -1
  111. package/dist/dashboard/assets/switch-SYm237HG.js.gz +0 -0
  112. package/dist/dashboard/assets/{useMutation-DjkLHUcE.js → useMutation-BQqCRNIi.js} +1 -1
  113. package/dist/dashboard/assets/useMutation-BQqCRNIi.js.gz +0 -0
  114. package/dist/dashboard/index.html +3 -2
  115. package/dist/dashboard/index.html.gz +0 -0
  116. package/dist/dashboard/precompressed-assets.json +118 -115
  117. package/migrations/full-postgres/0026_add_qoder_native_identity.sql +20 -0
  118. package/migrations/full-postgres/0027_qoder_identity_genuine_seed.sql +21 -0
  119. package/migrations/full-postgres/0028_add_family_template_key_telemetry.sql +40 -0
  120. package/migrations/full-postgres/0029_null_share_child_policy_columns.sql +38 -0
  121. package/migrations/full-postgres/0030_add_api_key_max_children.sql +10 -0
  122. package/migrations/full-postgres/0031_tenant_system_prompts.sql +16 -0
  123. package/migrations/full-postgres/0032_add_provider_pool_accounts.sql +53 -0
  124. package/migrations/full-postgres/0033_add_api_key_ban.sql +9 -0
  125. package/migrations/full-postgres/0034_add_api_key_ban_until.sql +7 -0
  126. package/migrations/full-postgres/0035_align_pool_account_status_enum.sql +21 -0
  127. package/migrations/lite-sqlite3/0026_add_qoder_native_identity.sql +15 -0
  128. package/migrations/lite-sqlite3/0027_qoder_identity_genuine_seed.sql +21 -0
  129. package/migrations/lite-sqlite3/0028_add_family_template_key_telemetry.sql +40 -0
  130. package/migrations/lite-sqlite3/0029_null_share_child_policy_columns.sql +38 -0
  131. package/migrations/lite-sqlite3/0030_add_api_key_max_children.sql +10 -0
  132. package/migrations/lite-sqlite3/0031_tenant_system_prompts.sql +10 -0
  133. package/migrations/lite-sqlite3/0032_add_provider_pool_accounts.sql +44 -0
  134. package/migrations/lite-sqlite3/0033_add_api_key_ban.sql +8 -0
  135. package/migrations/lite-sqlite3/0034_add_api_key_ban_until.sql +5 -0
  136. package/migrations/lite-sqlite3/0035_align_pool_account_status_enum.sql +6 -0
  137. package/package.json +2 -1
  138. package/src/boot/app.ts +9 -8
  139. package/src/config/network.ts +7 -3
  140. package/src/console/api-keys/bucket-spend.ts +7 -7
  141. package/src/console/api-keys/contracts.ts +475 -410
  142. package/src/console/api-keys/routes.ts +211 -23
  143. package/src/console/audit/routes.ts +3 -3
  144. package/src/console/auth/account-reset.ts +4 -4
  145. package/src/console/auth/session.ts +3 -3
  146. package/src/console/backup/contracts.ts +2 -0
  147. package/src/console/backup/export-endpoint.ts +14 -17
  148. package/src/console/backup/routes.ts +43 -11
  149. package/src/console/backup/service.ts +11 -6
  150. package/src/console/backup/store.ts +384 -88
  151. package/src/console/cli-tools/routes.ts +3 -3
  152. package/src/console/console-router-guards.ts +2 -2
  153. package/src/console/console-router.ts +4 -4
  154. package/src/console/dashboard-assets.ts +16 -1
  155. package/src/console/data-management/routes.ts +3 -3
  156. package/src/console/domain-registration.ts +4 -2
  157. package/src/console/observability/contracts.ts +4 -0
  158. package/src/console/observability/live.ts +3 -3
  159. package/src/console/observability/logs.ts +3 -3
  160. package/src/console/observability/performance.ts +3 -3
  161. package/src/console/observability/routes.ts +3 -3
  162. package/src/console/observability/store.ts +16 -16
  163. package/src/console/providers/catalog/contracts.ts +43 -22
  164. package/src/console/providers/catalog/provider-operations.ts +17 -2
  165. package/src/console/providers/catalog/routes.ts +3 -3
  166. package/src/console/providers/catalog/store-helpers.ts +349 -326
  167. package/src/console/providers/catalog/store.ts +1523 -1292
  168. package/src/console/providers/detail/routes.ts +3 -3
  169. package/src/console/providers/oauth/routes.ts +3 -3
  170. package/src/console/quota/account-quota-tenant.ts +34 -2
  171. package/src/console/quota/account-quota.ts +4 -1
  172. package/src/console/quota/account-test-egress.ts +4 -4
  173. package/src/console/quota/native-identity.ts +189 -0
  174. package/src/console/remote-support/contracts.ts +12 -6
  175. package/src/console/remote-support/service.ts +2 -2
  176. package/src/console/routing/model/routes.ts +3 -3
  177. package/src/console/routing/pools/contracts.ts +11 -8
  178. package/src/console/routing/pools/operations.ts +14 -7
  179. package/src/console/routing/pools/probe-verdict.ts +8 -3
  180. package/src/console/routing/pools/relay-deploy.ts +9 -9
  181. package/src/console/routing/pools/routes.ts +3 -3
  182. package/src/console/routing/pools/store.ts +21 -1
  183. package/src/console/settings/routes.ts +3 -3
  184. package/src/console/share/share-router.ts +51 -18
  185. package/src/console/share/share-stats.ts +158 -49
  186. package/src/console/share/share-usage.ts +24 -7
  187. package/src/console/shared/read-through-cache.ts +38 -2
  188. package/src/console/system-prompt/contracts.ts +102 -0
  189. package/src/console/system-prompt/routes.ts +165 -0
  190. package/src/console/updates/routes.ts +7 -4
  191. package/src/main.ts +11 -0
  192. package/src/network/outbound-fetch.ts +75 -4
  193. package/src/network/pool/resolver.ts +39 -39
  194. package/src/network/pool-cooldown-store.ts +71 -16
  195. package/src/network/pool-health-machine.ts +21 -1
  196. package/src/network/rate-limit-verdict.ts +68 -0
  197. package/src/network/ssrf.ts +4 -4
  198. package/src/observability/event-query.ts +437 -22
  199. package/src/observability/telemetry-status.ts +1 -1
  200. package/src/persistence/db-pg.ts +42 -0
  201. package/src/persistence/db-sqlite.ts +7 -2
  202. package/src/persistence/readiness.ts +18 -6
  203. package/src/persistence/schema.ts +162 -1
  204. package/src/persistence/stores/api-key-store.ts +849 -581
  205. package/src/persistence/stores/share-store.ts +65 -13
  206. package/src/persistence/stores/telemetry-store.ts +58 -2
  207. package/src/persistence/system-prompt-store.ts +104 -0
  208. package/src/protocol/codecs.ts +6 -6
  209. package/src/protocol/gateway-error.ts +6 -3
  210. package/src/protocol/image-generation.ts +2 -2
  211. package/src/protocol/reasoning.ts +65 -1
  212. package/src/protocol/request/chat.ts +1 -0
  213. package/src/protocol/response/chat.ts +2 -2
  214. package/src/protocol/response/codex.ts +1 -1
  215. package/src/protocol/response/gemini.ts +1 -1
  216. package/src/protocol/response/messages.ts +5 -5
  217. package/src/protocol/response/responses.ts +2 -2
  218. package/src/protocol/sse-decoder.ts +14 -3
  219. package/src/protocol/translation/capabilities.ts +2 -2
  220. package/src/protocol/upstream/send-claude-messages.ts +3 -3
  221. package/src/providers/adapters/base-adapter.ts +1 -1
  222. package/src/providers/adapters/compatible-adapter.ts +2 -2
  223. package/src/providers/authentication/oauth-refresh-service.ts +2 -2
  224. package/src/providers/configured-provider.ts +1 -1
  225. package/src/providers/discovery/probe-phases.ts +2 -2
  226. package/src/providers/discovery/probing-service.ts +12 -12
  227. package/src/providers/integrations/agentrouter/agentrouter.ts +2 -3
  228. package/src/providers/integrations/alibabacp/alibabacp.ts +2 -2
  229. package/src/providers/integrations/anthropic.ts +4 -11
  230. package/src/providers/integrations/antigravity/antigravity.ts +33 -47
  231. package/src/providers/integrations/buddy/buddy-catalog-shared.ts +1 -1
  232. package/src/providers/integrations/buddy/buddy-chat-shared.ts +30 -2
  233. package/src/providers/integrations/buddy/buddy-discovery-shared.ts +37 -6
  234. package/src/providers/integrations/buddy/buddy-identity.ts +22 -1
  235. package/src/providers/integrations/buddy/buddy-media.ts +21 -17
  236. package/src/providers/integrations/buddy/codebuddy/provider.ts +6 -2
  237. package/src/providers/integrations/buddy/workbuddy/provider.ts +6 -2
  238. package/src/providers/integrations/claude/claude-betas.ts +1 -1
  239. package/src/providers/integrations/claude/claude-credentials.ts +1 -1
  240. package/src/providers/integrations/claude/claude-slow-lane-retry.ts +1 -1
  241. package/src/providers/integrations/claude/claude.ts +3 -10
  242. package/src/providers/integrations/cline/cline.ts +8 -1
  243. package/src/providers/integrations/cloudflareai/cloudflareai.ts +4 -7
  244. package/src/providers/integrations/codex/codex-errors.ts +1 -1
  245. package/src/providers/integrations/codex/codex.ts +14 -15
  246. package/src/providers/integrations/commandcode/commandcode.ts +4 -4
  247. package/src/providers/integrations/commandcode/quota.ts +5 -1
  248. package/src/providers/integrations/cursor/cursor-http2.ts +11 -11
  249. package/src/providers/integrations/cursor/cursor.ts +15 -15
  250. package/src/providers/integrations/factory/factory.ts +4 -4
  251. package/src/providers/integrations/gemini.ts +11 -11
  252. package/src/providers/integrations/github/github.ts +26 -7
  253. package/src/providers/integrations/grok/grok.ts +2 -6
  254. package/src/providers/integrations/hermes.ts +5 -1
  255. package/src/providers/integrations/inferhub.ts +4 -11
  256. package/src/providers/integrations/kilo/kilo.ts +4 -0
  257. package/src/providers/integrations/kimi/kimi.ts +4 -11
  258. package/src/providers/integrations/kiro/kiro.ts +10 -10
  259. package/src/providers/integrations/muse/muse.ts +6 -9
  260. package/src/providers/integrations/openrouter/provider.ts +4 -0
  261. package/src/providers/integrations/perplexity.ts +2 -2
  262. package/src/providers/integrations/qoder/qoder-catalog.ts +47 -9
  263. package/src/providers/integrations/qoder/qoder-checkin.ts +242 -51
  264. package/src/providers/integrations/qoder/qoder-native-identity-store.ts +157 -0
  265. package/src/providers/integrations/qoder/qoder-oauth.ts +12 -3
  266. package/src/providers/integrations/qoder/qoder-realm.ts +15 -0
  267. package/src/providers/integrations/qoder/qoder-umid.ts +287 -0
  268. package/src/providers/integrations/qoder/qoder-virtual.ts +2 -2
  269. package/src/providers/integrations/qoder/qoder.ts +192 -24
  270. package/src/providers/integrations/tokenharbor.ts +2 -2
  271. package/src/providers/integrations/trae/trae.ts +6 -9
  272. package/src/providers/integrations/zcode/oauth.ts +1 -1
  273. package/src/providers/operations/account-error-classification.ts +6 -8
  274. package/src/providers/operations/account-health-service.ts +14 -16
  275. package/src/providers/operations/client-versions.ts +37 -22
  276. package/src/providers/operations/provider-catalog-service.ts +4 -4
  277. package/src/providers/operations/provider-credential-service.ts +16 -4
  278. package/src/providers/operations/upstream-deadline.ts +3 -3
  279. package/src/providers/provider-contracts.ts +55 -3
  280. package/src/providers/provider-credential-resolver.ts +1 -1
  281. package/src/providers/provider-registry.ts +4 -4
  282. package/src/providers/quota/quota-contracts.ts +24 -10
  283. package/src/providers/quota/refresh.ts +2 -2
  284. package/src/providers/search/search-provider.ts +5 -5
  285. package/src/security/abuse.ts +4 -4
  286. package/src/security/access-control.ts +19 -0
  287. package/src/security/admission/reasons.ts +17 -17
  288. package/src/security/api-key-auth.ts +18 -5
  289. package/src/shared/coerce.ts +19 -0
  290. package/src/transport/dispatch/attempt-finalize.ts +4 -3
  291. package/src/transport/dispatch/attempt-loop.ts +10 -7
  292. package/src/transport/dispatch/empty-stream.ts +6 -6
  293. package/src/transport/dispatch/generation.ts +9 -9
  294. package/src/transport/dispatch/leases.ts +8 -8
  295. package/src/transport/dispatch/proxy-request.ts +38 -9
  296. package/src/transport/dispatch/responses-compact.ts +11 -11
  297. package/src/transport/dispatch/retry-policy.ts +4 -67
  298. package/src/transport/dispatch/stream-bridge.ts +1 -1
  299. package/src/transport/dispatch/streaming/finalize.ts +9 -8
  300. package/src/transport/dispatch/streaming/stream.ts +16 -1
  301. package/src/transport/dispatch/systemone.ts +8 -8
  302. package/src/transport/dispatch/token-count.ts +9 -9
  303. package/src/transport/dispatch/upstream.ts +10 -0
  304. package/src/transport/dispatch/websearch.ts +6 -6
  305. package/src/transport/drain-endpoint.ts +2 -2
  306. package/src/transport/failure-policy.ts +13 -6
  307. package/src/transport/guards/auth-guard.ts +6 -5
  308. package/src/transport/guards/body-policy.ts +4 -4
  309. package/src/transport/guards/error-lifecycle.ts +18 -3
  310. package/src/transport/guards/guard-errors.ts +7 -3
  311. package/src/transport/guards/headroom-guard.ts +1 -1
  312. package/src/transport/guards/pipeline.ts +6 -6
  313. package/src/transport/guards/readiness-guard.ts +16 -2
  314. package/src/transport/guards/request-context.ts +3 -3
  315. package/src/transport/request/preparer.ts +37 -21
  316. package/src/transport/request/state.ts +4 -7
  317. package/src/transport/request/system-prompt-injection.ts +89 -0
  318. package/src/transport/request/tenant-preferences.ts +43 -0
  319. package/src/transport/routing/admission.ts +13 -3
  320. package/src/transport/routing/route-catalog.ts +104 -2
  321. package/src/transport/routing/route-model.ts +60 -7
  322. package/src/transport/routing/router.ts +306 -256
  323. package/src/transport/shutdown-notice.ts +19 -7
  324. package/src/transport/surface/completion.ts +1 -1
  325. package/src/transport/surface/messages/encode.ts +1 -1
  326. package/src/workers/daily-checkin.ts +2 -0
  327. package/dist/dashboard/assets/BannedPage-XaUmiBrC.js.gz +0 -0
  328. package/dist/dashboard/assets/CliToolDetailPage-CqD7QiLM.js +0 -1
  329. package/dist/dashboard/assets/CliToolDetailPage-CqD7QiLM.js.gz +0 -0
  330. package/dist/dashboard/assets/CliToolsPage-0XUo4UMn.js +0 -1
  331. package/dist/dashboard/assets/CliToolsPage-0XUo4UMn.js.gz +0 -0
  332. package/dist/dashboard/assets/CombosPage-CXGS_Ct-.js.gz +0 -0
  333. package/dist/dashboard/assets/ConsoleLogPage-DGnGNYnM.js +0 -1
  334. package/dist/dashboard/assets/ConsoleLogPage-DGnGNYnM.js.gz +0 -0
  335. package/dist/dashboard/assets/CustomizationPage-DAG5J9EY.js +0 -1
  336. package/dist/dashboard/assets/CustomizationPage-DAG5J9EY.js.gz +0 -0
  337. package/dist/dashboard/assets/HealthEventsModal-BKtAPYVF.js.gz +0 -0
  338. package/dist/dashboard/assets/LoginPage-BdmRLnq2.js.gz +0 -0
  339. package/dist/dashboard/assets/ModelPicker-BH8Tehdi.js.gz +0 -0
  340. package/dist/dashboard/assets/OverviewPage-CqMxibJB.js +0 -1
  341. package/dist/dashboard/assets/OverviewPage-CqMxibJB.js.gz +0 -0
  342. package/dist/dashboard/assets/ProviderDetailPage-CqMh2NRX.js +0 -4
  343. package/dist/dashboard/assets/ProviderDetailPage-CqMh2NRX.js.gz +0 -0
  344. package/dist/dashboard/assets/ProvidersPage-B9uWu4Nz.js +0 -1
  345. package/dist/dashboard/assets/ProvidersPage-B9uWu4Nz.js.gz +0 -0
  346. package/dist/dashboard/assets/ProxyPage-BoWOhpDO.js +0 -9
  347. package/dist/dashboard/assets/ProxyPage-BoWOhpDO.js.gz +0 -0
  348. package/dist/dashboard/assets/QuotaPage-7P2n--Fv.js +0 -1
  349. package/dist/dashboard/assets/QuotaPage-7P2n--Fv.js.gz +0 -0
  350. package/dist/dashboard/assets/SettingsPage-CxZ1fTkN.js +0 -1
  351. package/dist/dashboard/assets/SettingsPage-CxZ1fTkN.js.gz +0 -0
  352. package/dist/dashboard/assets/SetupPage-Co12Zzaw.js +0 -1
  353. package/dist/dashboard/assets/SetupPage-Co12Zzaw.js.gz +0 -0
  354. package/dist/dashboard/assets/SortableList-Dqf4zpS-.js.gz +0 -0
  355. package/dist/dashboard/assets/StudioPage-Byj2lAEm.js.gz +0 -0
  356. package/dist/dashboard/assets/UsagePage-CTkLAuRc.js +0 -56
  357. package/dist/dashboard/assets/UsagePage-CTkLAuRc.js.gz +0 -0
  358. package/dist/dashboard/assets/api-keys-CUi6UXsR.js +0 -1
  359. package/dist/dashboard/assets/api-keys-CUi6UXsR.js.gz +0 -0
  360. package/dist/dashboard/assets/app-KvsUJUMF.css +0 -2
  361. package/dist/dashboard/assets/app-KvsUJUMF.css.gz +0 -0
  362. package/dist/dashboard/assets/assert-67xXH5rf.js +0 -1
  363. package/dist/dashboard/assets/assert-67xXH5rf.js.gz +0 -0
  364. package/dist/dashboard/assets/badge-DC2L43Mj.js.gz +0 -0
  365. package/dist/dashboard/assets/banner-BBVcb0J3.js.gz +0 -0
  366. package/dist/dashboard/assets/card-Cca1XC6G.js.gz +0 -0
  367. package/dist/dashboard/assets/cli-tools-KvsWa1Jl.js +0 -1
  368. package/dist/dashboard/assets/cli-tools-KvsWa1Jl.js.gz +0 -0
  369. package/dist/dashboard/assets/clipboard-button-DUzEbynq.js.gz +0 -0
  370. package/dist/dashboard/assets/concurrency-BDF0ok7y.js.gz +0 -0
  371. package/dist/dashboard/assets/dialog-DFvWhwTa.js.gz +0 -0
  372. package/dist/dashboard/assets/drawer-CFM2sMX1.js +0 -1
  373. package/dist/dashboard/assets/drawer-CFM2sMX1.js.gz +0 -0
  374. package/dist/dashboard/assets/entry-Bqy8RFvM.js.gz +0 -0
  375. package/dist/dashboard/assets/entry-ClCMCWWQ.js +0 -4
  376. package/dist/dashboard/assets/entry-ClCMCWWQ.js.gz +0 -0
  377. package/dist/dashboard/assets/entry-_xHo5x6r.js +0 -1
  378. package/dist/dashboard/assets/entry-_xHo5x6r.js.gz +0 -0
  379. package/dist/dashboard/assets/formatters-CWWZ-uCZ.js.gz +0 -0
  380. package/dist/dashboard/assets/github-badge-C9e8Ts7e.js.gz +0 -0
  381. package/dist/dashboard/assets/index-DFVFrnWW.js.gz +0 -0
  382. package/dist/dashboard/assets/inline-BFUcvllj.js.gz +0 -0
  383. package/dist/dashboard/assets/input-C_ILAvBb.js.gz +0 -0
  384. package/dist/dashboard/assets/layout-CBIyT6Rt.js.gz +0 -0
  385. package/dist/dashboard/assets/list-row-ebJ_-Nsb.js.gz +0 -0
  386. package/dist/dashboard/assets/live-D0mm--ME.js +0 -1
  387. package/dist/dashboard/assets/live-D0mm--ME.js.gz +0 -0
  388. package/dist/dashboard/assets/minimize-2-D-MN8W8a.js +0 -1
  389. package/dist/dashboard/assets/minimize-2-D-MN8W8a.js.gz +0 -0
  390. package/dist/dashboard/assets/mutation-sMD965oO.js.gz +0 -0
  391. package/dist/dashboard/assets/numbers-BO9dKaFr.js.gz +0 -0
  392. package/dist/dashboard/assets/query-keys-CfL_Zdoh.js.gz +0 -0
  393. package/dist/dashboard/assets/quota-DZGyp0N5.js +0 -1
  394. package/dist/dashboard/assets/quota-DZGyp0N5.js.gz +0 -0
  395. package/dist/dashboard/assets/routing-BnIpi1IJ.js +0 -1
  396. package/dist/dashboard/assets/routing-BnIpi1IJ.js.gz +0 -0
  397. package/dist/dashboard/assets/select-DBNVLdUu.js.gz +0 -0
  398. package/dist/dashboard/assets/settings-BAFhDgX8.js +0 -1
  399. package/dist/dashboard/assets/settings-BAFhDgX8.js.gz +0 -0
  400. package/dist/dashboard/assets/stack-EMrOMt4I.js.gz +0 -0
  401. package/dist/dashboard/assets/state-CWoo7ty5.js.gz +0 -0
  402. package/dist/dashboard/assets/switch-CCjT6IkF.js.gz +0 -0
  403. package/dist/dashboard/assets/useMutation-DjkLHUcE.js.gz +0 -0
  404. /package/dist/dashboard/assets/{app-BaWd0puM.js → app-BayTTy01.js} +0 -0
  405. /package/dist/dashboard/assets/{app-BaWd0puM.js.gz → app-BayTTy01.js.gz} +0 -0
@@ -152,14 +152,14 @@ function finishImage(bytes: Uint8Array): CursorImageInput {
152
152
  throw new GatewayError(
153
153
  "invalid_request",
154
154
  400,
155
- "Cursor accepts PNG, JPEG, WebP and GIF image input only",
155
+ "Cursor accepts PNG, JPEG, WebP and GIF image input only (request {requestId}). Check the shape of what you sent — then resend 🪞",
156
156
  );
157
157
  }
158
158
  if (bytes.byteLength > MAX_IMAGE_BYTES) {
159
159
  throw new GatewayError(
160
160
  "invalid_request",
161
161
  400,
162
- `Cursor image input exceeds the ${MAX_IMAGE_BYTES}-byte limit`,
162
+ `Cursor image input exceeds the ${MAX_IMAGE_BYTES}-byte limit (request {requestId}). We love long reads, but not that long 📚`,
163
163
  );
164
164
  }
165
165
  return { data: bytes, mimeType };
@@ -183,7 +183,7 @@ async function resolveImageInput(
183
183
  if (dataUrl !== undefined) {
184
184
  const bytes = base64ToBytes(dataUrl.data);
185
185
  if (bytes === undefined) {
186
- throw new GatewayError("invalid_request", 400, "Cursor image attachment is not valid base64");
186
+ throw new GatewayError("invalid_request", 400, "Cursor image attachment is not valid base64 (request {requestId}). Check the shape of what you sent — then resend 🪞");
187
187
  }
188
188
  return finishImage(bytes);
189
189
  }
@@ -192,7 +192,7 @@ async function resolveImageInput(
192
192
  throw new GatewayError(
193
193
  "invalid_request",
194
194
  400,
195
- "Cursor image URLs must be HTTPS; private and non-TLS sources are not fetched",
195
+ "Cursor image URLs must be HTTPS; private and non-TLS sources are not fetched (request {requestId}). Check the shape of what you sent — then resend 🪞",
196
196
  );
197
197
  }
198
198
  const fetcher = context.outbound_attachment_fetch;
@@ -200,7 +200,7 @@ async function resolveImageInput(
200
200
  throw new GatewayError(
201
201
  "invalid_request",
202
202
  400,
203
- "Cursor has no validated egress to fetch this image URL",
203
+ "Cursor has no validated egress to fetch this image URL (request {requestId}). All roads blocked — time to find a new route 🗺️",
204
204
  );
205
205
  }
206
206
  // Redirects are not followed: reject the response rather than letting a
@@ -222,7 +222,7 @@ async function resolveImageInput(
222
222
  throw new GatewayError(
223
223
  "invalid_request",
224
224
  400,
225
- `Cursor image input exceeds the ${MAX_IMAGE_BYTES}-byte limit`,
225
+ `Cursor image input exceeds the ${MAX_IMAGE_BYTES}-byte limit (request {requestId}). We love long reads, but not that long 📚`,
226
226
  );
227
227
  }
228
228
  return finishImage(body);
@@ -503,7 +503,7 @@ class CursorAdapter implements ProviderAdapter {
503
503
  throw new GatewayError(
504
504
  "invalid_request",
505
505
  400,
506
- "This tool result cannot be matched to a live Cursor Run; resend the complete conversation.",
506
+ "This tool result cannot be matched to a live Cursor Run (request {requestId}); resend the complete conversation. The session went cold — reconnect or resend 🔌",
507
507
  );
508
508
  }
509
509
  const resolvedAll = pending.map((item) => cursorRunSessions.resolveCall(item.callId, scope));
@@ -530,7 +530,7 @@ class CursorAdapter implements ProviderAdapter {
530
530
  throw new GatewayError(
531
531
  "invalid_request",
532
532
  400,
533
- "This tool result refers to a Cursor tool call that is no longer pending. Resend the complete conversation.",
533
+ "This tool result refers to a Cursor tool call that is no longer pending (request {requestId}). Resend the complete conversation. The session went cold — reconnect or resend 🔌",
534
534
  { callIds: pending.map((item) => item.callId) },
535
535
  );
536
536
  }
@@ -538,12 +538,12 @@ class CursorAdapter implements ProviderAdapter {
538
538
  throw new GatewayError(
539
539
  "invalid_request",
540
540
  400,
541
- "These tool results belong to different Cursor Runs; resend them one Run at a time.",
541
+ "These tool results belong to different Cursor Runs (request {requestId}); resend them one Run at a time. The session went cold — reconnect or resend 🔌",
542
542
  );
543
543
  }
544
544
  const session = cursorRunSessions.get(runId);
545
545
  if (session === undefined) {
546
- throw new GatewayError("invalid_request", 400, "Cursor Run is no longer live");
546
+ throw new GatewayError("invalid_request", 400, "Cursor Run is no longer live (request {requestId}). The session went cold — reconnect or resend 🔌");
547
547
  }
548
548
  const handle = this.#handles.get(runId);
549
549
  if (handle === undefined) {
@@ -605,7 +605,7 @@ class CursorAdapter implements ProviderAdapter {
605
605
  throw new GatewayError(
606
606
  "admission_unavailable",
607
607
  503,
608
- "Cursor requires validated HTTP/2 egress; none is configured for this route",
608
+ "Cursor requires validated HTTP/2 egress (request {requestId}); none is configured for this route. No bridge to cross — configure a pool first 🏗️",
609
609
  { providerId: CURSOR_PROVIDER_ID },
610
610
  );
611
611
  }
@@ -784,7 +784,7 @@ class CursorAdapter implements ProviderAdapter {
784
784
  : new GatewayError(
785
785
  "platform_unavailable",
786
786
  502,
787
- `Cursor Run failed: ${error instanceof Error ? error.message : String(error)}`,
787
+ `Cursor Run failed (request {requestId}): ${error instanceof Error ? error.message : String(error)}. The upstream came back empty-handed — retry 🌙`,
788
788
  { providerId: CURSOR_PROVIDER_ID },
789
789
  "upstream",
790
790
  );
@@ -828,7 +828,7 @@ class CursorAdapter implements ProviderAdapter {
828
828
  throw new GatewayError(
829
829
  "platform_unavailable",
830
830
  502,
831
- `Cursor Run origin could not be resolved: ${error instanceof Error ? error.message : String(error)}`,
831
+ `Cursor Run origin could not be resolved (request {requestId}): ${error instanceof Error ? error.message : String(error)}. The connection ghosted us — come back in a moment 👻`,
832
832
  { providerId: CURSOR_PROVIDER_ID },
833
833
  "upstream",
834
834
  );
@@ -890,7 +890,7 @@ class CursorAdapter implements ProviderAdapter {
890
890
  }
891
891
  const message = item;
892
892
  if (context.abort_signal.aborted) {
893
- failure = new GatewayError("transport_closed", 499, "request was cancelled");
893
+ failure = new GatewayError("transport_closed", 499, "The request was cancelled (request {requestId}). Send it again whenever ready 🙃");
894
894
  break;
895
895
  }
896
896
  const execServer = recordOf(message["exec_server_message"]);
@@ -994,7 +994,7 @@ class CursorAdapter implements ProviderAdapter {
994
994
  : new GatewayError(
995
995
  "platform_unavailable",
996
996
  502,
997
- `Cursor Run failed: ${error instanceof Error ? error.message : String(error)}`,
997
+ `Cursor Run failed (request {requestId}): ${error instanceof Error ? error.message : String(error)}. The upstream came back empty-handed — retry 🌙`,
998
998
  { providerId: CURSOR_PROVIDER_ID },
999
999
  "upstream",
1000
1000
  );
@@ -270,7 +270,7 @@ export function factoryRegionUnavailable(modelId: string, region: string): Gatew
270
270
  return new GatewayError(
271
271
  "capability_unsupported",
272
272
  400,
273
- `factory: ${modelId} is unavailable in this account region (${region})`,
273
+ `factory: ${modelId} is unavailable in this account region (${region}) (request {requestId}). Know your tools — this one just isn't in the box 🧰`,
274
274
  { model: modelId, region, reason: "Factory Droid model is unavailable in this account region." },
275
275
  );
276
276
  }
@@ -351,7 +351,7 @@ export function factoryRoutingHeaders(
351
351
  function factoryEnvelope(context: ProviderDispatchContext): { token: string; orgId: string } {
352
352
  const secret = context.credential.secret;
353
353
  if (secret === undefined || secret.length === 0) {
354
- throw new GatewayError("invalid_request", 400, "factory: credential is missing a secret value");
354
+ throw new GatewayError("invalid_request", 400, "factory: credential is missing a secret value (request {requestId}). Wrong key, right door — check the credential 🔑");
355
355
  }
356
356
  try {
357
357
  const envelope = decodeFactoryDroidCredential(secret);
@@ -360,7 +360,7 @@ function factoryEnvelope(context: ProviderDispatchContext): { token: string; org
360
360
  throw new GatewayError(
361
361
  "invalid_request",
362
362
  400,
363
- `factory: ${error instanceof Error ? error.message : "stored credential is not a Factory Droid envelope"}`,
363
+ `factory: ${error instanceof Error ? error.message : "stored credential is not a Factory Droid envelope"} (request {requestId}). Wrong key, right door — check the credential 🔑`,
364
364
  );
365
365
  }
366
366
  }
@@ -523,7 +523,7 @@ class FactoryDroidAdapter implements ProviderAdapter {
523
523
  throw new GatewayError(
524
524
  "platform_unavailable",
525
525
  502,
526
- "factory gemini returned empty stream",
526
+ "factory gemini returned empty stream (request {requestId}). The upstream came back empty-handed — retry 🌙",
527
527
  {},
528
528
  "upstream",
529
529
  );
@@ -6,7 +6,7 @@ import { mapUpstreamHttpError } from "../../transport/failure-policy";
6
6
  import type { CanonicalEvent, CanonicalRequest, CanonicalStopReason } from "../../protocol/canonical-model";
7
7
  import { decodeSseEvents } from "../../protocol/sse-decoder";
8
8
  import { usageFromProvider } from "../provider-usage";
9
- import { readCredentialSecret, type ImageGenerationOutcome, type ProviderDispatchTarget, type ProviderAdapter, type ProviderDispatchContext, type WebSearchOutcome, type WebSearchResult } from "../provider-contracts";
9
+ import { readCredentialSecret, assertWireFamily, type ImageGenerationOutcome, type ProviderDispatchTarget, type ProviderAdapter, type ProviderDispatchContext, type WebSearchOutcome, type WebSearchResult } from "../provider-contracts";
10
10
  import type { ModelDefinition } from "../provider-model-definition";
11
11
  import { fetchOpenAICompatibleModels } from "../discovery/openai-model-discovery";
12
12
  import { isRecord } from "../../protocol/codecs";
@@ -181,7 +181,7 @@ class GeminiAdapter implements ProviderAdapter {
181
181
  context: ProviderDispatchContext,
182
182
  ): AsyncIterable<CanonicalEvent> {
183
183
  if (candidate.wire_family !== "chat" && candidate.wire_family !== "responses" && candidate.wire_family !== "messages") {
184
- throw new GatewayError("capability_unsupported", 400, `gemini supports chat/responses/messages, got ${candidate.wire_family}`);
184
+ assertWireFamily(candidate, ["chat", "responses", "messages"], "gemini");
185
185
  }
186
186
  const apiKey = readCredentialSecret(context.credential, "Gemini API key is required");
187
187
 
@@ -262,7 +262,7 @@ class GeminiAdapter implements ProviderAdapter {
262
262
  return;
263
263
  }
264
264
 
265
- if (!response.body) throw new GatewayError("platform_unavailable", 502, "Gemini returned empty stream", {}, "upstream");
265
+ if (!response.body) throw new GatewayError("platform_unavailable", 502, "Gemini returned empty stream (request {requestId}). The upstream came back empty-handed — retry 🌙", {}, "upstream");
266
266
  let seq = 1;
267
267
  yield { type: "response_start", sequence_number: seq++, model: request.model } as CanonicalEvent;
268
268
  const state = { started: true, stopped: false, activeCalls: new Set<string>() };
@@ -339,7 +339,7 @@ class GeminiAdapter implements ProviderAdapter {
339
339
  context: ProviderDispatchContext,
340
340
  ): Promise<WebSearchOutcome> {
341
341
  const query = typeof body.query === "string" ? body.query.trim() : "";
342
- if (!query) throw new GatewayError("invalid_request", 400, "search query is required");
342
+ if (!query) throw new GatewayError("invalid_request", 400, "search query is required (request {requestId}). Check the shape of what you sent — then resend 🪞");
343
343
  const rawMax = body.max_results;
344
344
  const maxResults =
345
345
  typeof rawMax === "number" && Number.isFinite(rawMax)
@@ -368,7 +368,7 @@ class GeminiAdapter implements ProviderAdapter {
368
368
  throw new GatewayError(
369
369
  "platform_unavailable",
370
370
  502,
371
- `Gemini search unreachable: ${error instanceof Error ? error.message : String(error)}`,
371
+ `Gemini search unreachable (request {requestId}): ${error instanceof Error ? error.message : String(error)}. The connection ghosted us — come back in a moment 👻`,
372
372
  );
373
373
  }
374
374
  if (!response.ok) throw await mapUpstreamHttpError(response, "gemini search");
@@ -376,11 +376,11 @@ class GeminiAdapter implements ProviderAdapter {
376
376
  try {
377
377
  data = await response.json();
378
378
  } catch {
379
- throw new GatewayError("platform_unavailable", 502, "Gemini search returned invalid JSON");
379
+ throw new GatewayError("platform_unavailable", 502, "Gemini search returned invalid JSON (request {requestId}). The upstream came back empty-handed — retry 🌙");
380
380
  }
381
381
  const results = normalizeGroundingResults(data, maxResults);
382
382
  if (results.length === 0)
383
- throw new GatewayError("platform_unavailable", 502, "Gemini search returned no grounded results");
383
+ throw new GatewayError("platform_unavailable", 502, "Gemini search returned no grounded results (request {requestId}). The upstream came back empty-handed — retry 🌙");
384
384
  return { results, total_results: results.length };
385
385
  }
386
386
 
@@ -391,7 +391,7 @@ class GeminiAdapter implements ProviderAdapter {
391
391
  ): Promise<ImageGenerationOutcome> {
392
392
  const prompt = imagePrompt(body);
393
393
  const model = candidate.model_id.length > 0 ? candidate.model_id : typeof body.model === "string" ? body.model : "";
394
- if (model.length === 0) throw new GatewayError("invalid_request", 400, "model is required");
394
+ if (model.length === 0) throw new GatewayError("invalid_request", 400, "model is required (request {requestId}). Check the shape of what you sent — then resend 🪞");
395
395
  const apiKey = readCredentialSecret(context.credential, "Gemini API key is required");
396
396
  const outboundFetch = context.outbound_fetch ?? this.fetchFn;
397
397
  const lifecycle = createUpstreamDeadlineLifecycle(context);
@@ -418,7 +418,7 @@ class GeminiAdapter implements ProviderAdapter {
418
418
  throw new GatewayError(
419
419
  "platform_unavailable",
420
420
  502,
421
- `Gemini image generation unreachable: ${error instanceof Error ? error.message : String(error)}`,
421
+ `Gemini image generation unreachable (request {requestId}): ${error instanceof Error ? error.message : String(error)}. The connection ghosted us — come back in a moment 👻`,
422
422
  {},
423
423
  "upstream",
424
424
  );
@@ -430,11 +430,11 @@ class GeminiAdapter implements ProviderAdapter {
430
430
  try {
431
431
  data = await response.json();
432
432
  } catch {
433
- throw new GatewayError("platform_unavailable", 502, "Gemini image generation returned invalid JSON", {}, "upstream");
433
+ throw new GatewayError("platform_unavailable", 502, "Gemini image generation returned invalid JSON (request {requestId}). The upstream came back empty-handed — retry 🌙", {}, "upstream");
434
434
  }
435
435
  const images = geminiInlineImages(data);
436
436
  if (images.length === 0) {
437
- throw new GatewayError("platform_unavailable", 502, "Gemini image generation returned no image", {}, "upstream");
437
+ throw new GatewayError("platform_unavailable", 502, "Gemini image generation returned no image (request {requestId}). The upstream came back empty-handed — retry 🌙", {}, "upstream");
438
438
  }
439
439
  return { created: imageCreatedSeconds(), data: images };
440
440
  }
@@ -288,6 +288,23 @@ interface GithubAutoDecision {
288
288
  readonly sessionToken: string;
289
289
  readonly expiresAt: number;
290
290
  readonly hasImage: boolean;
291
+ readonly route?: GithubModelRoute;
292
+ }
293
+
294
+ function routeFromAutoModel(value: unknown): GithubModelRoute | undefined {
295
+ if (!isRecord(value) || !Array.isArray(value["supported_endpoints"])) return undefined;
296
+ const endpoints = new Set(
297
+ value["supported_endpoints"]
298
+ .filter((endpoint): endpoint is string => typeof endpoint === "string")
299
+ .map((endpoint) => endpoint.replace(/^\/v1(?=\/)/, "")),
300
+ );
301
+ if (endpoints.has("/chat/completions")) {
302
+ return { wireFamily: "chat", endpointPath: GITHUB_ENDPOINT_PATH };
303
+ }
304
+ if (endpoints.has("/responses")) {
305
+ return { wireFamily: "responses", endpointPath: GITHUB_RESPONSES_ENDPOINT_PATH };
306
+ }
307
+ return undefined;
291
308
  }
292
309
 
293
310
  interface CopilotModelOptions {
@@ -394,7 +411,7 @@ class GithubAdapter extends OpenAICompatibleAdapter {
394
411
 
395
412
  const secret = context.credential.secret;
396
413
  if (secret === undefined || secret.length === 0) {
397
- throw new GatewayError("authentication_failed", 401, "GitHub Copilot credential is missing");
414
+ throw new GatewayError("authentication_failed", 401, "GitHub Copilot credential is missing (request {requestId}). Wrong key, right door — check the credential 🔑");
398
415
  }
399
416
  const credential = new TextDecoder().decode(secret);
400
417
  const autoInput = autoPrompt(request);
@@ -407,7 +424,7 @@ class GithubAdapter extends OpenAICompatibleAdapter {
407
424
  : this.#getCachedAutoDecision(cacheKey, autoInput.hasImage);
408
425
  if (autoDecision === undefined) {
409
426
  if (autoInput.prompt.length === 0) {
410
- throw new GatewayError("invalid_request", 400, "Copilot Auto requires a text prompt");
427
+ throw new GatewayError("invalid_request", 400, "Copilot Auto requires a text prompt (request {requestId}). Check the shape of what you sent — then resend 🪞");
411
428
  }
412
429
  autoDecision = await this.#selectAutoModel(
413
430
  autoInput.prompt,
@@ -420,7 +437,7 @@ class GithubAdapter extends OpenAICompatibleAdapter {
420
437
  if (cacheKey !== undefined) this.#cacheAutoDecision(cacheKey, autoDecision);
421
438
  }
422
439
  const selectedModelId = autoDecision.modelId;
423
- let route = await githubModelRoute(selectedModelId, credential);
440
+ let route = autoDecision.route ?? await githubModelRoute(selectedModelId, credential);
424
441
  if (route === undefined) {
425
442
  // dynamic-import: discovery module is only loaded when the first
426
443
  // request routes to a model id not in the bundled roster; off the hot
@@ -445,7 +462,7 @@ class GithubAdapter extends OpenAICompatibleAdapter {
445
462
  throw new GatewayError(
446
463
  "upstream_unprocessable",
447
464
  502,
448
- "Copilot Auto selected a model without a discovered inference endpoint",
465
+ "Copilot Auto selected a model without a discovered inference endpoint (request {requestId}). The upstream came back empty-handed — retry 🌙",
449
466
  { selected_model: selectedModelId },
450
467
  "upstream",
451
468
  );
@@ -481,7 +498,7 @@ class GithubAdapter extends OpenAICompatibleAdapter {
481
498
  ? undefined
482
499
  : parseGithubCredential(new TextDecoder().decode(context.credential.secret));
483
500
  if (credential === undefined) {
484
- throw new GatewayError("authentication_failed", 401, "GitHub Copilot credential is missing");
501
+ throw new GatewayError("authentication_failed", 401, "GitHub Copilot credential is missing (request {requestId}). Wrong key, right door — check the credential 🔑");
485
502
  }
486
503
  const payload: Record<string, unknown> = { prompt, tier };
487
504
  if (hasImage) payload.has_image = true;
@@ -505,7 +522,7 @@ class GithubAdapter extends OpenAICompatibleAdapter {
505
522
  throw new GatewayError(
506
523
  "upstream_unprocessable",
507
524
  502,
508
- "Copilot Auto returned an invalid routing response",
525
+ "Copilot Auto returned an invalid routing response (request {requestId}). The upstream came back empty-handed — retry 🌙",
509
526
  {},
510
527
  "upstream",
511
528
  );
@@ -528,7 +545,7 @@ class GithubAdapter extends OpenAICompatibleAdapter {
528
545
  throw new GatewayError(
529
546
  "upstream_unprocessable",
530
547
  502,
531
- "Copilot Auto did not return a selected model",
548
+ "Copilot Auto did not return a selected model (request {requestId}). The upstream came back empty-handed — retry 🌙",
532
549
  {},
533
550
  "upstream",
534
551
  );
@@ -539,11 +556,13 @@ class GithubAdapter extends OpenAICompatibleAdapter {
539
556
  Number.isFinite(responsePayload.expires_at)
540
557
  ? responsePayload.expires_at
541
558
  : 0;
559
+ const route = routeFromAutoModel(responsePayload.selected_model);
542
560
  return {
543
561
  modelId: responsePayload.selected_model.id.trim(),
544
562
  sessionToken: responsePayload.session_token.trim(),
545
563
  expiresAt,
546
564
  hasImage,
565
+ ...(route === undefined ? {} : { route }),
547
566
  };
548
567
  }
549
568
 
@@ -9,12 +9,12 @@
9
9
  * OpenAI-compatible adapter.
10
10
  */
11
11
  import { randomBytes, randomUUID } from "node:crypto";
12
- import { GatewayError } from "../../../protocol/gateway-error";
13
12
  import type { CanonicalEvent, CanonicalRequest } from "../../../protocol/canonical-model";
14
13
  import {
15
14
  OpenAICompatibleAdapter,
16
15
  withBearerAuthentication,
17
16
  } from "../../adapters/compatible-adapter";
17
+ import { assertCredentialKind } from "../../provider-contracts";
18
18
  import type { ProviderAdapter, ProviderDispatchTarget, ProviderDispatchContext } from "../../provider-contracts";
19
19
  import { providerBaseUrl } from "../../provider-metadata";
20
20
  import { boundedUpstreamArray, boundedUpstreamNumber, sanitizeUpstreamLabel } from "../../provider-sanitize";
@@ -526,11 +526,7 @@ class GrokAdapter extends OpenAICompatibleAdapter {
526
526
  context: ProviderDispatchContext,
527
527
  ): AsyncIterable<CanonicalEvent> {
528
528
  if (context.credential.credential_kind !== "oauth") {
529
- throw new GatewayError(
530
- "invalid_request",
531
- 400,
532
- "grok requires an xAI OAuth device credential",
533
- );
529
+ assertCredentialKind(context.credential, "oauth", "grok");
534
530
  }
535
531
  yield* super.dispatch(request, candidate, context);
536
532
  }
@@ -4,6 +4,7 @@ import type { ApiKeyProviderSpec } from "../configured-provider";
4
4
  import { isRecord } from "../../protocol/codecs";
5
5
  import { boundedUpstreamArray, boundedUpstreamNumber, sanitizeUpstreamLabel } from "../provider-sanitize";
6
6
  import { getHermesVersion } from "../operations/client-versions";
7
+ import { clineReasoningReplayPrepares } from "./buddy/buddy-chat-shared";
7
8
 
8
9
  const HERMES_PROVIDER_ID = "hermes" as const;
9
10
 
@@ -110,7 +111,10 @@ export const HERMES_MODELS: readonly ModelDefinition[] = [
110
111
  export const HERMES_SPEC: ApiKeyProviderSpec = {
111
112
  provider_id: HERMES_PROVIDER_ID,
112
113
  endpoint_paths_by_wire_family: {},
113
- prePayload: injectHermesTags,
114
+ prePayload: (payload, request, candidate) => {
115
+ injectHermesTags(payload, request, candidate);
116
+ for (const prepare of clineReasoningReplayPrepares) prepare(payload);
117
+ },
114
118
  // The origin sits behind Cloudflare, which answers a request carrying no
115
119
  // `user-agent` with 403 "Attention Required!" while the same request with any
116
120
  // non-empty UA answers 200 — measured directly against the API: an empty UA
@@ -1,6 +1,7 @@
1
1
  // Hybrid: the Messages-wire leg (`InferhubMessagesAdapter`, Claude Messages
2
2
  // envelope + Bearer auth + breakpoint cache stamping) is a non-OpenAI wire and
3
3
  // stays bespoke; the chat leg is a declarative spec row.
4
+ import { assertCredentialKind, assertWireFamily } from "../provider-contracts";
4
5
  import type { ProviderDispatchTarget, ProviderAdapter, ProviderDispatchContext } from "../provider-contracts";
5
6
  import type { ModelDefinition } from "../provider-model-definition";
6
7
  import type { ProviderModelDiscovery } from "../discovery/discovery-types";
@@ -326,21 +327,13 @@ export class InferhubMessagesAdapter implements ProviderAdapter {
326
327
  context: ProviderDispatchContext,
327
328
  ): AsyncIterable<CanonicalEvent> {
328
329
  if (candidate.wire_family !== "messages") {
329
- throw new GatewayError(
330
- "capability_unsupported",
331
- 400,
332
- `inferhub messages leg supports messages only, got ${candidate.wire_family}`,
333
- );
330
+ assertWireFamily(candidate, "messages", "inferhub messages leg");
334
331
  }
335
332
  if (context.credential.credential_kind !== "api_key") {
336
- throw new GatewayError(
337
- "invalid_request",
338
- 400,
339
- `inferhub: api_key credential required (got credential_kind=${context.credential.credential_kind})`,
340
- );
333
+ assertCredentialKind(context.credential, "api_key", "inferhub");
341
334
  }
342
335
  if (context.credential.secret === undefined || context.credential.secret.length === 0) {
343
- throw new GatewayError("invalid_request", 400, "inferhub: API key is missing a secret value");
336
+ throw new GatewayError("invalid_request", 400, "inferhub: API key is missing a secret value (request {requestId}). Wrong key, right door — check the key 🔑");
344
337
  }
345
338
  const apiKey = new TextDecoder().decode(context.credential.secret);
346
339
  const customHeaders = filterClaudeCustomHeaders({
@@ -24,6 +24,7 @@ import type { CanonicalRequest } from "../../../protocol/canonical-model";
24
24
  import { OpenAICompatibleAdapter, withBearerAuthentication } from "../../adapters/compatible-adapter";
25
25
  import { providerBaseUrl } from "../../provider-metadata";
26
26
  import { decodeKiloCredential } from "../../../security/credential-codec";
27
+ import { clineReasoningReplayPrepares } from "../buddy/buddy-chat-shared";
27
28
 
28
29
  export const KILO_PROVIDER_ID = "kilo" as const;
29
30
  export const KILO_BASE_URL = providerBaseUrl("kilo");
@@ -117,6 +118,9 @@ class KiloAdapter extends OpenAICompatibleAdapter {
117
118
  provider_id: KILO_PROVIDER_ID,
118
119
  base_url: KILO_BASE_URL,
119
120
  endpoint_paths_by_wire_family: { chat: KILO_ENDPOINT_PATH },
121
+ prePayload: (payload) => {
122
+ for (const prepare of clineReasoningReplayPrepares) prepare(payload);
123
+ },
120
124
  ...(fetchImpl ? { fetchImpl } : {}),
121
125
  }),
122
126
  );
@@ -26,6 +26,7 @@ import {
26
26
  } from "../../../protocol/request/messages";
27
27
  import { sendClaudeMessagesRequest } from "../../../protocol/upstream/send-claude-messages";
28
28
  import { endpointUrl } from "../../../protocol/codecs";
29
+ import { assertCredentialKind, assertWireFamily } from "../../provider-contracts";
29
30
  import type { ProviderDispatchTarget, ProviderAdapter, ProviderDispatchContext } from "../../provider-contracts";
30
31
  import {
31
32
  getKimiCommonHeaders,
@@ -141,24 +142,16 @@ class KimiCodeAdapter implements ProviderAdapter {
141
142
  context: ProviderDispatchContext,
142
143
  ): AsyncIterable<CanonicalEvent> {
143
144
  if (candidate.wire_family !== "messages") {
144
- throw new GatewayError(
145
- "capability_unsupported",
146
- 400,
147
- `kimi supports messages only, got ${candidate.wire_family}`,
148
- );
145
+ assertWireFamily(candidate, "messages", "kimi");
149
146
  }
150
147
  if (context.credential.credential_kind !== "oauth") {
151
- throw new GatewayError(
152
- "invalid_request",
153
- 400,
154
- `kimi: OAuth credential required (got credential_kind=${context.credential.credential_kind})`,
155
- );
148
+ assertCredentialKind(context.credential, "oauth", "kimi");
156
149
  }
157
150
  if (!context.credential.secret) {
158
151
  throw new GatewayError(
159
152
  "authentication_failed",
160
153
  401,
161
- "kimi: OAuth credential secret is required",
154
+ "kimi: OAuth credential secret is required (request {requestId}). Wrong key, right door — check the credential 🔑",
162
155
  );
163
156
  }
164
157
  const credential = parseKimiCredential(
@@ -344,7 +344,7 @@ class KiroAdapter implements ProviderAdapter {
344
344
  ? new TextDecoder().decode(context.credential.secret).trim()
345
345
  : "";
346
346
  if (secret.length === 0) {
347
- throw new GatewayError("authentication_failed", 401, "Missing Kiro credential", {
347
+ throw new GatewayError("authentication_failed", 401, "Missing Kiro credential (request {requestId}). Wrong key, right door — check the credential 🔑", {
348
348
  providerId: KIRO_PROVIDER_ID,
349
349
  });
350
350
  }
@@ -381,7 +381,7 @@ class KiroAdapter implements ProviderAdapter {
381
381
  throw new GatewayError(
382
382
  "invalid_request",
383
383
  400,
384
- "Kiro cannot accept this conversation",
384
+ "Kiro cannot accept this conversation (request {requestId}). Check the shape of what you sent — then resend 🪞",
385
385
  { providerId: KIRO_PROVIDER_ID, problems: built.error.problems },
386
386
  );
387
387
  }
@@ -398,7 +398,7 @@ class KiroAdapter implements ProviderAdapter {
398
398
  machineId,
399
399
  );
400
400
  if (!response.body) {
401
- throw new GatewayError("platform_unavailable", 502, "Kiro returned an empty body", {
401
+ throw new GatewayError("platform_unavailable", 502, "Kiro returned an empty body (request {requestId}). The upstream came back empty-handed — retry 🌙", {
402
402
  providerId: KIRO_PROVIDER_ID,
403
403
  }, "upstream");
404
404
  }
@@ -433,14 +433,14 @@ class KiroAdapter implements ProviderAdapter {
433
433
  });
434
434
  } catch (error) {
435
435
  if (error instanceof Error && error.name === "AbortError") {
436
- throw new GatewayError("transport_closed", 499, "request was cancelled", {
436
+ throw new GatewayError("transport_closed", 499, "The request was cancelled (request {requestId}). Send it again whenever ready 🙃", {
437
437
  providerId: KIRO_PROVIDER_ID,
438
438
  });
439
439
  }
440
440
  throw new GatewayError(
441
441
  "transport_unavailable",
442
442
  502,
443
- `Kiro transport failed: ${error instanceof Error ? error.message : String(error)}`,
443
+ `Kiro transport failed (request {requestId}): ${error instanceof Error ? error.message : String(error)}. The connection ghosted us — come back in a moment 👻`,
444
444
  { providerId: KIRO_PROVIDER_ID },
445
445
  "network",
446
446
  );
@@ -484,7 +484,7 @@ class KiroAdapter implements ProviderAdapter {
484
484
  throw new GatewayError(
485
485
  "platform_unavailable",
486
486
  502,
487
- "Kiro EventStream exceeded the buffer bound",
487
+ "Kiro EventStream exceeded the buffer bound (request {requestId}). The upstream sent more than we can hold — retry 🌙",
488
488
  { providerId: KIRO_PROVIDER_ID },
489
489
  "upstream",
490
490
  );
@@ -497,7 +497,7 @@ class KiroAdapter implements ProviderAdapter {
497
497
  throw new GatewayError(
498
498
  "platform_unavailable",
499
499
  502,
500
- `Kiro EventStream is corrupt: ${decoded.failure.detail}`,
500
+ `Kiro EventStream is corrupt: ${decoded.failure.detail} (request {requestId}). The upstream sent something we can't read — retry 🌙`,
501
501
  { providerId: KIRO_PROVIDER_ID, reason: decoded.failure.reason },
502
502
  "upstream",
503
503
  );
@@ -506,7 +506,7 @@ class KiroAdapter implements ProviderAdapter {
506
506
  for (const message of decoded.messages) {
507
507
  const outcome = decoder.decode(message);
508
508
  if (outcome.failure !== undefined) {
509
- throw new GatewayError("platform_unavailable", 502, outcome.failure.message, {
509
+ throw new GatewayError("platform_unavailable", 502, `${outcome.failure.message} (request {requestId}). The upstream came back empty-handed — retry 🌙`, {
510
510
  providerId: KIRO_PROVIDER_ID,
511
511
  }, "upstream");
512
512
  }
@@ -578,14 +578,14 @@ class KiroAdapter implements ProviderAdapter {
578
578
  await reader.cancel().catch(() => undefined);
579
579
  if (error instanceof GatewayError) throw error;
580
580
  if (error instanceof Error && error.name === "AbortError") {
581
- throw new GatewayError("transport_closed", 499, "request was cancelled", {
581
+ throw new GatewayError("transport_closed", 499, "The request was cancelled (request {requestId}). Send it again whenever ready 🙃", {
582
582
  providerId: KIRO_PROVIDER_ID,
583
583
  });
584
584
  }
585
585
  throw new GatewayError(
586
586
  "platform_unavailable",
587
587
  502,
588
- `Kiro stream failed: ${error instanceof Error ? error.message : String(error)}`,
588
+ `Kiro stream failed (request {requestId}): ${error instanceof Error ? error.message : String(error)}. The upstream came back empty-handed — retry 🌙`,
589
589
  { providerId: KIRO_PROVIDER_ID },
590
590
  "upstream",
591
591
  );
@@ -22,6 +22,7 @@ import type { CanonicalRequest } from "../../../protocol/canonical-model";
22
22
  import { GatewayError } from "../../../protocol/gateway-error";
23
23
  import { decodeMuseCredential } from "../../../security/credential-codec";
24
24
  import { OpenAICompatibleAdapter, withBearerAuthentication } from "../../adapters/compatible-adapter";
25
+ import { assertCredentialKind } from "../../provider-contracts";
25
26
  import type { ProviderDispatchTarget, ProviderDispatchContext, ResolvedCredential } from "../../provider-contracts";
26
27
  import { providerBaseUrl } from "../../provider-metadata";
27
28
 
@@ -85,19 +86,19 @@ export function parseMuseCodeCredential(
85
86
  // as before the extraction.
86
87
  const message = error instanceof Error ? error.message : "";
87
88
  if (message.endsWith("must be a JSON object")) {
88
- throw new GatewayError("invalid_request", 400, "muse: credential must be a JSON object");
89
+ throw new GatewayError("invalid_request", 400, "muse: credential must be a JSON object (request {requestId}). Wrong key, right door — check the credential 🔑");
89
90
  }
90
91
  if (message.endsWith("is not a valid JSON blob")) {
91
92
  throw new GatewayError(
92
93
  "invalid_request",
93
94
  400,
94
- "muse: credential is not a valid JSON blob; sign in again",
95
+ "muse: credential is not a valid JSON blob (request {requestId}); sign in again. Wrong key, right door — check the credential 🔑",
95
96
  );
96
97
  }
97
98
  throw new GatewayError(
98
99
  "authentication_failed",
99
100
  401,
100
- "muse: credential is missing oauthAccessToken or apiKey; sign in again",
101
+ "muse: credential is missing oauthAccessToken or apiKey (request {requestId}); sign in again. Wrong key, right door — check the credential 🔑",
101
102
  );
102
103
  }
103
104
  }
@@ -137,17 +138,13 @@ class MuseCodeAdapter extends OpenAICompatibleAdapter {
137
138
  candidate: ProviderDispatchTarget,
138
139
  ): Promise<Record<string, string>> {
139
140
  if (context.credential.credential_kind !== "oauth") {
140
- throw new GatewayError(
141
- "invalid_request",
142
- 400,
143
- `muse: OAuth credential required (got credential_kind=${context.credential.credential_kind})`,
144
- );
141
+ assertCredentialKind(context.credential, "oauth", "muse");
145
142
  }
146
143
  if (!context.credential.secret) {
147
144
  throw new GatewayError(
148
145
  "authentication_failed",
149
146
  401,
150
- "muse: OAuth credential secret is required",
147
+ "muse: OAuth credential secret is required (request {requestId}). Wrong key, right door — check the credential 🔑",
151
148
  );
152
149
  }
153
150
  const { apiKey } = decodeMuseCredential(context.credential.secret);
@@ -5,6 +5,7 @@ import { providerBaseUrl } from "../../provider-metadata";
5
5
  import type { DiscoveryInput } from "../../discovery/discovery-types";
6
6
  import { defineModel } from "../../provider-model-definition";
7
7
  import { openrouterFreeRoster } from "../../discovery/openrouter-free-roster";
8
+ import { clineReasoningReplayPrepares } from "../buddy/buddy-chat-shared";
8
9
 
9
10
  const OPENROUTER_PROVIDER_ID = "openrouter" as const;
10
11
 
@@ -112,6 +113,9 @@ export const OPENROUTER_SPEC: ApiKeyProviderSpec = {
112
113
  provider_id: OPENROUTER_PROVIDER_ID,
113
114
  endpoint_paths_by_wire_family: {},
114
115
  extra_headers: OPENROUTER_REQUIRED_HEADERS,
116
+ prePayload: (payload) => {
117
+ for (const prepare of clineReasoningReplayPrepares) prepare(payload);
118
+ },
115
119
  };
116
120
 
117
121
  export async function discoverOpenrouterModels(