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
@@ -23,14 +23,19 @@ import type {
23
23
  import { toolResultParts, toolCallParts } from "../../../protocol/canonical-model";
24
24
  import { decodeSseEvents } from "../../../protocol/sse-decoder";
25
25
  import { usageFromProvider, readReasoningText } from "../../provider-usage";
26
- import { readCredentialSecret, type ProviderDispatchTarget, type ProviderAdapter, type ProviderDispatchContext, type ProviderId } from "../../provider-contracts";
26
+ import { readCredentialSecret, assertWireFamily, type ProviderDispatchTarget, type ProviderAdapter, type ProviderDispatchContext, type ProviderId } from "../../provider-contracts";
27
27
  import type { ModelDefinition } from "../../provider-model-definition";
28
28
  import { parseQoderOAuthState } from "./qoder-oauth";
29
29
  import { getQoderVersion } from "../../operations/client-versions";
30
30
  import { abortGatewayError, createUpstreamDeadlineLifecycle } from "../../operations/upstream-deadline";
31
31
  import { qoderMachineOs } from "../../operations/cli-platform";
32
- import { qoderInferenceCandidates, QODER_CHAT_PATH, QODER_REALMS, type QoderRealm } from "./qoder-realm";
33
- import { QODER_CN_MODEL_CONFIGS, QODER_INTL_MODEL_CONFIGS, QODER_INTL_MODELS } from "./qoder-catalog";
32
+ import { qoderIdeChatUrl, qoderInferenceCandidates, QODER_CHAT_PATH, QODER_REALMS, type QoderRealm } from "./qoder-realm";
33
+ import {
34
+ QODER_CN_MODEL_CONFIGS,
35
+ QODER_INTL_MODEL_CONFIGS,
36
+ QODER_INTL_MODELS,
37
+ qoderDiscoveredMaxContexts,
38
+ } from "./qoder-catalog";
34
39
  import { isQoderVirtualModel, qoderDispatchModelId } from "./qoder-virtual";
35
40
  import {
36
41
  LEAK_MARKER_ANY_RE,
@@ -46,7 +51,7 @@ const LEAK_HOLD_MAX_CHARS = 32_768;
46
51
  const LEAK_HOLD_MAX_FRAMES = 200;
47
52
 
48
53
  const BAD_ECHO_MESSAGE =
49
- "Qoder returned an unparseable tool-call echo; no tools were emitted";
54
+ "Qoder returned an unparseable tool-call echo (request {requestId}); no tools were emitted. The upstream came back empty-handed — retry 🌙";
50
55
 
51
56
  function badEchoError(): GatewayError {
52
57
  return new GatewayError(
@@ -246,7 +251,7 @@ export function qoderOAuthAuth(
246
251
  throw new GatewayError(
247
252
  "authentication_failed",
248
253
  401,
249
- "Qoder OAuth account is missing its user identity (auth_state.userId/machineId absent or empty — re-login, or re-export with authState included); sign in again",
254
+ "Qoder OAuth account is missing its user identity (request {requestId}) (auth_state.userId/machineId absent or empty — re-login, or re-export with authState included); sign in again. Wrong key, right door — check the credential 🔑",
250
255
  );
251
256
  }
252
257
  return {
@@ -387,14 +392,14 @@ async function exchangeQoderPat(
387
392
  try {
388
393
  responseBody = await response.json();
389
394
  } catch {
390
- throw new GatewayError("platform_unavailable", 502, "Qoder PAT exchange returned invalid JSON", {}, "upstream");
395
+ throw new GatewayError("platform_unavailable", 502, "Qoder PAT exchange returned invalid JSON (request {requestId}). The upstream came back empty-handed — retry 🌙", {}, "upstream");
391
396
  }
392
397
  if (typeof responseBody !== "object" || responseBody === null || Array.isArray(responseBody)) {
393
- throw new GatewayError("platform_unavailable", 502, "Qoder PAT exchange returned unexpected response", {}, "upstream");
398
+ throw new GatewayError("platform_unavailable", 502, "Qoder PAT exchange returned unexpected response (request {requestId}). The upstream came back empty-handed — retry 🌙", {}, "upstream");
394
399
  }
395
400
  const rec = responseBody as Record<string, unknown>;
396
401
  if (typeof rec["id"] !== "string" || typeof rec["securityOauthToken"] !== "string") {
397
- throw new GatewayError("platform_unavailable", 502, "Qoder PAT exchange returned incomplete credentials", {}, "upstream");
402
+ throw new GatewayError("platform_unavailable", 502, "Qoder PAT exchange returned incomplete credentials (request {requestId}). The upstream came back empty-handed — retry 🌙", {}, "upstream");
398
403
  }
399
404
  return {
400
405
  userId: rec["id"] as string,
@@ -481,7 +486,7 @@ function qoderEnvelopeToFrames(data: string): Array<{ event: string | null; data
481
486
  throw new GatewayError(
482
487
  classification.code,
483
488
  classification.status,
484
- detail.length > 0 ? detail : `Qoder stream envelope returned HTTP ${status}`,
489
+ detail.length > 0 ? `${detail} (request {requestId}). The upstream came back empty-handed — retry 🌙` : `Qoder stream envelope returned HTTP ${status} (request {requestId}). The upstream came back empty-handed — retry 🌙`,
485
490
  {
486
491
  providerId: QODER_PROVIDER_ID,
487
492
  providerStatus: status,
@@ -498,8 +503,11 @@ function qoderEnvelopeToFrames(data: string): Array<{ event: string | null; data
498
503
  }
499
504
  const body = rec["body"];
500
505
  if (body === "[DONE]") return [{ event: null, data: "[DONE]" }];
501
- if (typeof body !== "string") return [];
502
- return [{ event: null, data: body.replace(/\r?\n/g, "") }];
506
+ if (typeof body === "string") return [{ event: null, data: body.replace(/\r?\n/g, "") }];
507
+ // Direct OpenAI chunk (IDE surface): no envelope body, but a `choices` array
508
+ // is present. Pass the raw data through so the chunk parser sees it.
509
+ if (Array.isArray(rec["choices"])) return [{ event: null, data }];
510
+ return [];
503
511
  }
504
512
 
505
513
  function qoderStableHash(prefix: string, ...parts: string[]): string {
@@ -540,6 +548,21 @@ function qoderStableRecordId(modelId: string, messages: CanonicalRequest["messag
540
548
  return h.digest("hex").slice(0, 16);
541
549
  }
542
550
 
551
+ /**
552
+ * The widest selectable context window for one model key.
553
+ *
554
+ * The live list's `context_config` is the authority when discovery has run:
555
+ * it names every window the account can switch to, and the widest one is what
556
+ * a dispatch should send (`model_config.max_input_tokens`) so a conversation is
557
+ * never capped at the *currently active* window (often 180k) when the account
558
+ * can use 1M. Falls back to the static roster's stated window otherwise.
559
+ */
560
+ function qoderMaxContextFor(modelId: string, modelConfig: QoderModelConfig, realm: QoderRealm): number {
561
+ const discovered = qoderDiscoveredMaxContexts(realm).get(modelId);
562
+ if (discovered !== undefined) return discovered;
563
+ return modelConfig.max_input_tokens;
564
+ }
565
+
543
566
  function qoderRequestedMaxTokens(request: CanonicalRequest, modelConfig: QoderModelConfig): number {
544
567
  /**
545
568
  * The ceiling is the roster's stated one. A fixed 32k fallback would clamp
@@ -927,7 +950,11 @@ export function buildQoderRequest(
927
950
  url: "",
928
951
  is_vl: modelConfig.is_vl,
929
952
  is_reasoning: modelConfig.is_reasoning,
930
- max_input_tokens: modelConfig.max_input_tokens,
953
+ // Send the widest selectable window from the live list's `context_config`
954
+ // (falling back to the static roster's stated window). Without this, the
955
+ // request would declare the *currently active* window — often 180k — even
956
+ // when the account can use 1M, capping the conversation the upstream sees.
957
+ max_input_tokens: qoderMaxContextFor(modelId, modelConfig, realm),
931
958
  // Mirrors the roster's stated ceiling. A fixed 32k fallback would be sent
932
959
  // for every model alike, so the request would declare a cap the catalog
933
960
  // does not publish.
@@ -951,9 +978,89 @@ export function buildQoderRequest(
951
978
  };
952
979
  }
953
980
 
981
+ /**
982
+ * Builds the modern model-server (`/model/v1/chat/completions`) request body.
983
+ *
984
+ * It reuses the message translation from `buildQoderRequest` (tool turns,
985
+ * reasoning echo, image parts) and wraps the result in the OpenAI-shaped body
986
+ * the IDE surface expects: top-level `model`, `messages`, `stream_options`,
987
+ * and a `metadata.context`/`metadata.business` block. `model` is the roster
988
+ * key after virtual-name resolution, so the upstream never sees a virtual id
989
+ * (which it answers by falling back to `auto`).
990
+ */
991
+ export function buildQoderIdeRequest(
992
+ modelId: string,
993
+ request: CanonicalRequest,
994
+ modelConfig: QoderModelConfig,
995
+ auth: QoderAuth,
996
+ realm: QoderRealm = "intl",
997
+ version = getQoderVersion(),
998
+ ): Record<string, unknown> {
999
+ const legacy = buildQoderRequest(modelId, request, modelConfig, auth, realm);
1000
+ const messages = legacy.messages as Array<Record<string, unknown>>;
1001
+ return {
1002
+ model: modelId,
1003
+ messages,
1004
+ stream: true,
1005
+ stream_options: { include_usage: true },
1006
+ metadata: {
1007
+ context: {
1008
+ request_id: legacy.request_id,
1009
+ request_set_id: legacy.request_set_id,
1010
+ session_id: legacy.session_id,
1011
+ source_session_id: "",
1012
+ task_id: "common",
1013
+ client_type: "5",
1014
+ },
1015
+ business: {
1016
+ business_product: "cli",
1017
+ business_type: "agent",
1018
+ business_version: version,
1019
+ scene: "assistant",
1020
+ },
1021
+ },
1022
+ max_tokens: (legacy.max_tokens as number) ?? 65_536,
1023
+ };
1024
+ }
1025
+
1026
+ /** Identity headers for the modern model-server surface (plain bearer). */
1027
+ function qoderIdeHeaders(auth: QoderAuth, version: string): Record<string, string> {
1028
+ return {
1029
+ Authorization: `Bearer ${auth.securityOauthToken}`,
1030
+ "Content-Type": "application/json",
1031
+ Accept: "text/event-stream",
1032
+ "X-Request-ID": randomUUID(),
1033
+ "X-Session-ID": randomUUID(),
1034
+ "Cosy-Version": version,
1035
+ "Cosy-ClientType": "5",
1036
+ "Cosy-MachineOS": qoderMachineOs(),
1037
+ "Cosy-MachineHostname": "DESKTOP-QODER",
1038
+ "Cosy-MachineId": auth.machineId,
1039
+ "Cosy-MachineToken": auth.machineId,
1040
+ "Cosy-MachineType": "5",
1041
+ };
1042
+ }
1043
+
1044
+ async function callQoderIde(
1045
+ url: string,
1046
+ body: Record<string, unknown>,
1047
+ auth: QoderAuth,
1048
+ signal: AbortSignal,
1049
+ fetcher: QoderFetcher,
1050
+ version = getQoderVersion(),
1051
+ ): Promise<Response> {
1052
+ return fetcher(url, {
1053
+ method: "POST",
1054
+ headers: qoderIdeHeaders(auth, version),
1055
+ body: JSON.stringify(body),
1056
+ signal,
1057
+ });
1058
+ }
1059
+
954
1060
  // Stream transformation — decode SSE, unwrap COSY envelope, map OpenAI chat chunks to CanonicalEvent
955
1061
 
956
1062
  function mapQoderFinishReason(raw: unknown): CanonicalStopReason | undefined {
1063
+ if (raw === "stop") return "stop";
957
1064
  if (raw === "length") return "length";
958
1065
  if (raw === "tool_calls") return "tool_use";
959
1066
  if (raw === "content_filter") return "content_filter";
@@ -1002,7 +1109,39 @@ export async function* qoderBodyToCanonicalEvents(
1002
1109
  } catch {
1003
1110
  continue;
1004
1111
  }
1112
+ // IDE surface error event: `{"code":"invalid_model_error","message":"..."}`
1113
+ // carries no choices. Surface it as an upstream failure instead of an
1114
+ // empty successful stream.
1115
+ if (json["error"] !== undefined && json["choices"] === undefined) {
1116
+ const error = json["error"] as Record<string, unknown> | string;
1117
+ const message =
1118
+ typeof error === "string"
1119
+ ? error
1120
+ : typeof error?.["message"] === "string"
1121
+ ? (error["message"] as string)
1122
+ : typeof json["message"] === "string"
1123
+ ? (json["message"] as string)
1124
+ : "Qoder model server error";
1125
+ const code = typeof json["code"] === "string" ? (json["code"] as string) : undefined;
1126
+ throw new GatewayError(
1127
+ code === "invalid_model_error" ? "model_not_found" : "platform_unavailable",
1128
+ code === "invalid_model_error" ? 404 : 502,
1129
+ `${message} (request {requestId}). The upstream came back empty-handed — retry 🌙`,
1130
+ { providerId: QODER_PROVIDER_ID, ...(code === undefined ? {} : { providerCode: code }) },
1131
+ "upstream",
1132
+ );
1133
+ }
1005
1134
  if (json["usage"] && typeof json["usage"] === "object") rawUsage = json["usage"] as Record<string, unknown>;
1135
+ // IDE surface terminal event: `choices:[]` + `raw_usage`. It carries the
1136
+ // final usage and confirms the turn ended, so record it and synthesize a
1137
+ // stop reason — the terminal event has no `choices[].finish_reason`.
1138
+ if (json["raw_usage"] && typeof json["raw_usage"] === "object") {
1139
+ const rawUsageObj = json["raw_usage"] as Record<string, unknown>;
1140
+ rawUsage = (rawUsageObj["data"] as Record<string, unknown> | undefined) ?? rawUsageObj;
1141
+ if (json["choices"] === undefined || (json["choices"] as unknown[]).length === 0) {
1142
+ finishReason = finishReason ?? "stop";
1143
+ }
1144
+ }
1006
1145
  const choices = (json["choices"] as Array<Record<string, unknown>>) ?? [];
1007
1146
  for (const choice of choices) {
1008
1147
  const delta = choice["delta"] as Record<string, unknown> | undefined;
@@ -1128,12 +1267,16 @@ export async function* qoderBodyToCanonicalEvents(
1128
1267
  }
1129
1268
  }
1130
1269
  const fr = choice["finish_reason"];
1131
- if (fr !== null && fr !== undefined) finishReason = fr;
1270
+ // The IDE surface sends an empty string (`""`) for the per-delta finish
1271
+ // reason, which is not a real stop signal — only a value that maps to a
1272
+ // canonical stop reason (or the terminal `[DONE]` frame) ends the turn.
1273
+ if (fr !== null && fr !== undefined && fr !== "") finishReason = fr;
1132
1274
  // Some providers send usage in choices[0].usage or top-level
1133
1275
  if (choice["usage"] && typeof choice["usage"] === "object") rawUsage = choice["usage"] as Record<string, unknown>;
1134
1276
  }
1135
- // Some Qoder chunks carry finish_reason at top level
1136
- if (json["finish_reason"] !== undefined) finishReason = json["finish_reason"];
1277
+ // Some Qoder chunks carry finish_reason at top level (empty string means
1278
+ // no stop signal, same as the per-delta field).
1279
+ if (json["finish_reason"] !== undefined && json["finish_reason"] !== "") finishReason = json["finish_reason"];
1137
1280
  }
1138
1281
  }
1139
1282
  // End of stream: recover a held-back marker+JSON echo. A valid echo becomes
@@ -1168,7 +1311,12 @@ export async function* qoderBodyToCanonicalEvents(
1168
1311
  }
1169
1312
  }
1170
1313
  }
1171
- // If no terminal arrived, synthesize one (prevents truncation error)
1314
+ // A `[DONE]` frame from the IDE surface may be the only terminal signal (the
1315
+ // per-delta `finish_reason` is empty and `raw_usage` is not always sent), so
1316
+ // a clean end-of-stream with no finish reason is a normal stop, not a
1317
+ // truncation. If no terminal arrived at all, still synthesize one (prevents
1318
+ // truncation error).
1319
+ if (hasTerminal && finishReason === undefined) finishReason = "stop";
1172
1320
  const stopReason = mapQoderFinishReason(finishReason);
1173
1321
  const _providerStop = typeof finishReason === "string" ? finishReason : undefined;
1174
1322
  void hasTerminal;
@@ -1204,16 +1352,28 @@ interface QoderAdapterOptions {
1204
1352
  readonly realm?: QoderRealm;
1205
1353
  /** Bundled provider id, so the adapter's own id matches the route's. */
1206
1354
  readonly providerId?: string;
1355
+ /**
1356
+ * Which chat surface to dispatch on.
1357
+ *
1358
+ * `ide` (default) uses the modern model-server surface
1359
+ * (`/model/v1/chat/completions`, plain bearer, OpenAI-shaped chunks): it
1360
+ * streams immediately. The legacy `cosy` surface (`/algo/.../agent_chat_generation`)
1361
+ * answers `10605 isQueued` and parks requests in a server queue, so it is a
1362
+ * fallback, not a default.
1363
+ */
1364
+ readonly surface?: "ide" | "cosy";
1207
1365
  }
1208
1366
 
1209
1367
  class QoderAdapter implements ProviderAdapter {
1210
1368
  readonly provider_id: ProviderId;
1211
1369
  private readonly realm: QoderRealm;
1370
+ private readonly surface: "ide" | "cosy";
1212
1371
  private readonly fetchFn: typeof fetch;
1213
1372
 
1214
1373
  constructor(options: QoderAdapterOptions = {}) {
1215
1374
  this.provider_id = (options.providerId ?? QODER_PROVIDER_ID) as ProviderId;
1216
1375
  this.realm = options.realm ?? "intl";
1376
+ this.surface = options.surface ?? "ide";
1217
1377
  this.fetchFn = options.fetch ?? globalThis.fetch;
1218
1378
  }
1219
1379
 
@@ -1236,7 +1396,7 @@ class QoderAdapter implements ProviderAdapter {
1236
1396
  context: ProviderDispatchContext,
1237
1397
  ): AsyncIterable<CanonicalEvent> {
1238
1398
  if (candidate.wire_family !== "chat") {
1239
- throw new GatewayError("capability_unsupported", 400, `qoder supports chat only, got ${candidate.wire_family}`);
1399
+ assertWireFamily(candidate, "chat", "qoder");
1240
1400
  }
1241
1401
  const secret = readCredentialSecret(context.credential, "Qoder credential is required");
1242
1402
 
@@ -1253,7 +1413,7 @@ class QoderAdapter implements ProviderAdapter {
1253
1413
  throw new GatewayError(
1254
1414
  "model_not_found",
1255
1415
  404,
1256
- `Qoder virtual model ${requestedModelId} resolves to a roster key this realm does not serve`,
1416
+ `Qoder virtual model ${requestedModelId} resolves to a roster key this realm does not serve (request {requestId}). Try another model — this one's off the menu 📚`,
1257
1417
  { virtual_model: requestedModelId, target: qoderDispatchModelId(requestedModelId) },
1258
1418
  "upstream",
1259
1419
  );
@@ -1283,7 +1443,6 @@ class QoderAdapter implements ProviderAdapter {
1283
1443
  const auth = isQoderPat(secret)
1284
1444
  ? await exchangeQoderPat(secret, lifecycle.signal, fetcher, version, this.realm)
1285
1445
  : qoderOAuthAuth(secret, context.credential.auth_state, context.credential.account_id);
1286
- const qoderBody = buildQoderRequest(modelId, request, modelConfig, auth, this.realm);
1287
1446
  // A full URL from the route wins, but the fallback is this realm's
1288
1447
  // gateway — not the intl host hardcoded into `MODERN_PROFILE`. Dispatching
1289
1448
  // a CN credential to `*.qoder.sh` is refused outright. The host order
@@ -1292,11 +1451,18 @@ class QoderAdapter implements ProviderAdapter {
1292
1451
  const url =
1293
1452
  candidate.endpoint_path && candidate.endpoint_path.startsWith("http")
1294
1453
  ? candidate.endpoint_path
1295
- : `${qoderInferenceCandidates(this.realm, auth.securityOauthToken)[0] ?? ""}${QODER_CHAT_PATH}`;
1454
+ : this.surface === "ide"
1455
+ ? qoderIdeChatUrl(this.realm)
1456
+ : `${qoderInferenceCandidates(this.realm, auth.securityOauthToken)[0] ?? ""}${QODER_CHAT_PATH}`;
1296
1457
 
1297
- const response = await callQoder(url, qoderBody, modelId, auth, lifecycle.signal, fetcher, version);
1458
+ // The modern IDE surface streams immediately and needs no COSY signing;
1459
+ // the legacy COSY surface is kept as an explicit fallback.
1460
+ const response =
1461
+ this.surface === "ide"
1462
+ ? await callQoderIde(url, buildQoderIdeRequest(modelId, request, modelConfig, auth, this.realm, version), auth, lifecycle.signal, fetcher, version)
1463
+ : await callQoder(url, buildQoderRequest(modelId, request, modelConfig, auth, this.realm), modelId, auth, lifecycle.signal, fetcher, version);
1298
1464
  if (!response.ok) throw await mapUpstreamHttpError(response, "qoder");
1299
- if (!response.body) throw new GatewayError("platform_unavailable", 502, "Qoder returned empty body", {}, "upstream");
1465
+ if (!response.body) throw new GatewayError("platform_unavailable", 502, "Qoder returned empty body (request {requestId}). The upstream came back empty-handed — retry 🌙", {}, "upstream");
1300
1466
  // Headers arrived: the pre-stream deadline has served its purpose.
1301
1467
  // From here the gateway stall/first-chunk watchdog (propagated via
1302
1468
  // context.abort_signal) governs the body. Keeping this timer armed
@@ -71,11 +71,11 @@ export const TOKENHARBOR_FALLBACK_MODELS: readonly ModelDefinition[] = [
71
71
  function tokenharborPrePayload(payload: Record<string, unknown>): void {
72
72
  const model = payload["model"];
73
73
  if (typeof model !== "string" || model.length === 0) {
74
- throw new GatewayError("invalid_request", 400, "TokenHarbor model is required");
74
+ throw new GatewayError("invalid_request", 400, "TokenHarbor model is required (request {requestId}). Check the shape of what you sent — then resend 🪞");
75
75
  }
76
76
  // Strip accidental query-string smuggling (mirrors the factory's own guard)
77
77
  if (model.includes("?")) {
78
- throw new GatewayError("invalid_request", 400, "TokenHarbor model must not contain query string");
78
+ throw new GatewayError("invalid_request", 400, "TokenHarbor model must not contain query string (request {requestId}). Check the shape of what you sent — then resend 🪞");
79
79
  }
80
80
  }
81
81
 
@@ -10,6 +10,7 @@ import type { UsageRecord } from "../../../protocol/canonical-model";
10
10
  import { usageFromProvider } from "../../provider-usage";
11
11
  import {
12
12
  readCredentialSecret,
13
+ assertWireFamily,
13
14
  type ProviderAdapter,
14
15
  type ProviderDispatchContext,
15
16
  type ProviderDispatchTarget,
@@ -67,7 +68,7 @@ const STOP_REASONS: Readonly<Record<string, CanonicalStopReason>> = {
67
68
  function traeAuth(secret: string, accountId: string | undefined): TraeAuth {
68
69
  const token = extractToken(secret);
69
70
  if (token === undefined) {
70
- throw new GatewayError("authentication_failed", 401, "Trae credential has no access token", {}, "upstream");
71
+ throw new GatewayError("authentication_failed", 401, "Trae credential has no access token (request {requestId}). Wrong key, right door — check the credential 🔑", {}, "upstream");
71
72
  }
72
73
  return { token, uid: accountId ?? "" };
73
74
  }
@@ -116,7 +117,7 @@ function throwTraeUpstreamError(codeRaw: unknown, messageRaw: unknown): never {
116
117
  throw new GatewayError(
117
118
  "authentication_failed",
118
119
  401,
119
- `Trae could not authenticate the account (upstream code 1001): ${message}`,
120
+ `Trae could not authenticate the account (upstream code 1001): ${message} (request {requestId}). Wrong key, right door — check the credential 🔑`,
120
121
  { providerCode: "1001", providerStatus: 1001, credentialEvidence: true },
121
122
  "upstream",
122
123
  );
@@ -127,7 +128,7 @@ function throwTraeUpstreamError(codeRaw: unknown, messageRaw: unknown): never {
127
128
  throw new GatewayError(
128
129
  "quota_exceeded",
129
130
  429,
130
- `Trae quota exceeded (upstream code ${code ?? "unknown"}): ${message}`,
131
+ `Trae quota exceeded (upstream code ${code ?? "unknown"}): ${message} (request {requestId}). You've maxed this window — pace yourself ⏳`,
131
132
  { providerCode: code ?? "4008", providerStatus: code === "4008" ? 4008 : 429 },
132
133
  "upstream",
133
134
  );
@@ -152,11 +153,7 @@ class TraeAdapter implements ProviderAdapter {
152
153
  context: ProviderDispatchContext,
153
154
  ): AsyncIterable<CanonicalEvent> {
154
155
  if (candidate.wire_family !== "chat") {
155
- throw new GatewayError(
156
- "capability_unsupported",
157
- 400,
158
- `trae supports chat only, got ${candidate.wire_family}`,
159
- );
156
+ assertWireFamily(candidate, "chat", "trae");
160
157
  }
161
158
  const secret = readCredentialSecret(context.credential, "Trae credential is required");
162
159
  const modelId = candidate.model_id || request.model;
@@ -214,7 +211,7 @@ class TraeAdapter implements ProviderAdapter {
214
211
  });
215
212
  if (!response.ok) throw await mapUpstreamHttpError(response, this.provider_id);
216
213
  if (!response.body) {
217
- throw new GatewayError("platform_unavailable", 502, "Trae returned empty body", {}, "upstream");
214
+ throw new GatewayError("platform_unavailable", 502, "Trae returned empty body (request {requestId}). The upstream came back empty-handed — retry 🌙", {}, "upstream");
218
215
  }
219
216
  // Headers arrived, so the pre-stream deadline has served its purpose;
220
217
  // from here the gateway's own stall watchdog governs the body.
@@ -78,7 +78,7 @@ function unwrapEnvelope(body: unknown, operation: string): unknown {
78
78
  // codebase is 401. A 400 told the client to fix its body when the real
79
79
  // action is to fix the credential — and HTTP itself reserves 401 for
80
80
  // exactly this, so a standard client reads the class from the status.
81
- throw new GatewayError("authentication_failed", 401, `Z.AI ${message}`, { providerId: "zcode" });
81
+ throw new GatewayError("authentication_failed", 401, `Z.AI ${message} (request {requestId}). Wrong key, right door — check the credential 🔑`, { providerId: "zcode" });
82
82
  }
83
83
  }
84
84
  return root.data ?? body;
@@ -290,10 +290,10 @@ export async function registerByokProviders(
290
290
  try {
291
291
  providerId = parseProviderId(row.id);
292
292
  } catch {
293
- throw new GatewayError("invalid_request", 400, `invalid BYOK provider ID: ${row.id}`);
293
+ throw new GatewayError("invalid_request", 400, `invalid BYOK provider ID: ${row.id} (request {requestId}). Check the shape of what you sent — then resend 🪞`);
294
294
  }
295
295
  if (isBundledProviderId(providerId)) {
296
- throw new GatewayError("invalid_request", 409, `BYOK provider ID collides with a built-in: ${row.id}`, {
296
+ throw new GatewayError("invalid_request", 409, `BYOK provider ID collides with a built-in: ${row.id} (request {requestId}). This name's taken — pick another alias ✨`, {
297
297
  provider_id: providerId,
298
298
  });
299
299
  }
@@ -309,14 +309,14 @@ export async function registerByokProviders(
309
309
  }
310
310
  } catch (error) {
311
311
  if (error instanceof GatewayError) throw error;
312
- throw new GatewayError("invalid_request", 400, `invalid BYOK base URL for provider ${row.id}`);
312
+ throw new GatewayError("invalid_request", 400, `invalid BYOK base URL for provider ${row.id} (request {requestId}). Check the shape of what you sent — then resend 🪞`);
313
313
  }
314
314
  // IP literals are rejected at registration; DNS names are revalidated at dispatch.
315
315
  if (isIP(parsedUrl.hostname) !== 0 && !isAddressAllowed(parsedUrl.hostname, ssrfPolicy)) {
316
316
  throw new GatewayError(
317
317
  "invalid_request",
318
318
  400,
319
- `BYOK provider ${row.id} host is blocked (private/unsafe address)`,
319
+ `BYOK provider ${row.id} host is blocked (private/unsafe address) (request {requestId}). All roads blocked — time to find a new route 🗺️`,
320
320
  );
321
321
  }
322
322
  const defaultPort = parsedUrl.protocol === "http:" ? 80 : 443;
@@ -9,8 +9,20 @@ import type { OAuthRefreshService, OAuthTokenRefresher } from "../authentication
9
9
  import { CredentialResolver, type CredentialAlternative } from "../provider-credential-resolver";
10
10
  import { parseProviderId, type CredentialKind } from "../provider-contracts";
11
11
  import type { ResolvedCredential } from "../provider-contracts";
12
- import { record } from "../authentication/oauth-flow-store";
13
12
  import { refreshLeadMs } from "./oauth-refresh-lead";
13
+ import { asJsonbRecord } from "../../shared/coerce";
14
+
15
+ /**
16
+ * The `auth_state` JSONB column arrives as a POJO from Postgres (drizzle
17
+ * jsonb) but as a JSON STRING from SQLite (the native column stores text).
18
+ * The resolver's `record()` helper rejects strings, so a SQLite read dropped
19
+ * the auth state entirely — Qoder dispatch then saw no userId/machineId and
20
+ * failed with 401 "missing its user identity". Parse the string form once
21
+ * here so every backend hands the resolver the same POJO shape.
22
+ */
23
+ function jsonbRecord(value: unknown): Record<string, unknown> | undefined {
24
+ return asJsonbRecord(value);
25
+ }
14
26
 
15
27
  // ── Credential cache ─────────────────────────────────────────────────────────
16
28
  /**
@@ -229,7 +241,7 @@ export async function resolveCredentialForAccount(
229
241
  throw new GatewayError(
230
242
  "authentication_failed",
231
243
  401,
232
- "provider credential cannot be decrypted; re-save the account credential",
244
+ "provider credential cannot be decrypted (request {requestId}); re-save the account credential. Wrong key, right door — check the credential 🔑",
233
245
  {
234
246
  providerId: row.providerId,
235
247
  accountId: row.id,
@@ -253,7 +265,7 @@ export async function resolveCredentialForAccount(
253
265
  }
254
266
  }
255
267
 
256
- const authState = record(row.authState);
268
+ const authState = jsonbRecord(row.authState);
257
269
  const alternative: CredentialAlternative = {
258
270
  provider_id: parseProviderId(row.providerId),
259
271
  account_id: row.id,
@@ -319,7 +331,7 @@ export async function resolveAccountCredentialsForExport(
319
331
  await oauth.refreshService.ensureFreshAccessToken(accountId, refresher);
320
332
  account = await loadAccountWithFreshness(db, accountId);
321
333
  if (!account) {
322
- throw new GatewayError("admission_unavailable", 503, "This provider account no longer exists, so its credential cannot be read. Re-select the account, or point the route at a live one.", {
334
+ throw new GatewayError("admission_unavailable", 503, "This provider account no longer exists, so its credential cannot be read (request {requestId}). Re-select the account, or point the route at a live one. The account went cold — reconnect or resend 🔌", {
323
335
  provider_id: providerId,
324
336
  account_id: accountId,
325
337
  });
@@ -17,7 +17,7 @@ export function createUpstreamDeadlineLifecycle(
17
17
  new GatewayError(
18
18
  "deadline_exceeded",
19
19
  504,
20
- "upstream request deadline exceeded",
20
+ "upstream request deadline exceeded (request {requestId}). Some things just take longer 🐢",
21
21
  {},
22
22
  "upstream",
23
23
  ),
@@ -52,11 +52,11 @@ export function abortGatewayError(
52
52
  const reason = lifecycle.signal.reason ?? requestSignal?.reason;
53
53
  if (reason instanceof GatewayError) return reason;
54
54
  if (reason instanceof DOMException && reason.name === "TimeoutError")
55
- return new GatewayError("deadline_exceeded", 504, "request deadline exceeded", {}, "cartethyia");
55
+ return new GatewayError("deadline_exceeded", 504, "request deadline exceeded (request {requestId}). Some things just take longer 🐢", {}, "cartethyia");
56
56
  const abortLike =
57
57
  (error instanceof DOMException && error.name === "AbortError") ||
58
58
  (error instanceof Error && error.name === "AbortError");
59
59
  if (lifecycle.signal.aborted || requestSignal?.aborted || abortLike)
60
- return new GatewayError("transport_closed", 499, "request was cancelled");
60
+ return new GatewayError("transport_closed", 499, "The request was cancelled (request {requestId}). Send it again whenever ready 🙃");
61
61
  return undefined;
62
62
  }
@@ -42,12 +42,56 @@ export function readCredentialSecret(credential: ResolvedCredential, errorMessag
42
42
  throw new GatewayError(
43
43
  "authentication_failed",
44
44
  401,
45
- errorMessage ?? "Provider credential secret is missing or empty",
45
+ `${errorMessage ?? "Provider credential secret is missing or empty"} (request {requestId}). Wrong key, right door — check the credential 🔑`,
46
46
  );
47
47
  }
48
48
  return raw;
49
49
  }
50
50
 
51
+ /**
52
+ * Asserts a credential is of the required kind, throwing a typed rejection
53
+ * otherwise. Providers disagree on the kind they accept (`oauth` vs
54
+ * `api_key` vs `scoped_access_token`), and each hand-rolled its own check +
55
+ * message. This centralizes the common pattern so a mis-bound credential reads
56
+ * the same everywhere, preserving the existing `invalid_request`/400 wire
57
+ * contract.
58
+ */
59
+ export function assertCredentialKind(
60
+ credential: ResolvedCredential,
61
+ expected: CredentialKind | readonly CredentialKind[],
62
+ providerLabel: string,
63
+ ): void {
64
+ const expectedKinds = Array.isArray(expected) ? expected : [expected];
65
+ if (expectedKinds.includes(credential.credential_kind)) return;
66
+ throw new GatewayError(
67
+ "invalid_request",
68
+ 400,
69
+ `${providerLabel}: ${expectedKinds.join(" or ")} credential required (got credential_kind=${credential.credential_kind}) (request {requestId}). Wrong key, right door — check the credential 🔑`,
70
+ { expected_credential_kind: expectedKinds },
71
+ );
72
+ }
73
+
74
+ /**
75
+ * Asserts a candidate's wire family is one of the ones the provider serves,
76
+ * throwing `capability_unsupported` otherwise. Every provider hand-rolled this
77
+ * guard with the same shape; this centralizes the pattern so a mis-routed
78
+ * candidate reads the same everywhere.
79
+ */
80
+ export function assertWireFamily(
81
+ candidate: ProviderDispatchTarget,
82
+ allowed: WireFamily | readonly WireFamily[],
83
+ providerLabel: string,
84
+ ): void {
85
+ const allowedFamilies = Array.isArray(allowed) ? allowed : [allowed];
86
+ if (allowedFamilies.includes(candidate.wire_family)) return;
87
+ throw new GatewayError(
88
+ "capability_unsupported",
89
+ 400,
90
+ `${providerLabel} supports ${allowedFamilies.join("/")} only, got ${candidate.wire_family} (request {requestId}). Know your tools — this one just isn't in the box 🧰`,
91
+ { expected_wire_families: allowedFamilies, got_wire_family: candidate.wire_family },
92
+ );
93
+ }
94
+
51
95
  /** Fetch capability that validates DNS answers and redirects before each request. */
52
96
  export type ValidatedOutboundFetch = (
53
97
  input: RequestInfo | URL,
@@ -352,7 +396,7 @@ export interface WebSearchOutcome {
352
396
  function normalizeProviderSlug(id: string): string {
353
397
  const trimmed = id.trim().toLowerCase();
354
398
  if (!/^[a-z][a-z0-9-]{0,63}$/.test(trimmed)) {
355
- throw new GatewayError("invalid_request", 400, "Provider ID must be a lowercase slug");
399
+ throw new GatewayError("invalid_request", 400, "Provider ID must be a lowercase slug (request {requestId}). Check the shape of what you sent — then resend 🪞");
356
400
  }
357
401
  return trimmed;
358
402
  }
@@ -379,7 +423,7 @@ export function parseCustomProviderId(value: string): CustomProviderId {
379
423
  throw new GatewayError(
380
424
  "slug_reserved",
381
425
  409,
382
- "The provider ID is reserved for a builtin provider",
426
+ "The provider ID is reserved for a builtin provider (request {requestId}). This name's taken — pick another alias ✨",
383
427
  { provider_id: normalized },
384
428
  );
385
429
  }
@@ -68,7 +68,7 @@ export class CredentialResolver {
68
68
  throw new GatewayError(
69
69
  "invalid_request",
70
70
  401,
71
- "No usable upstream credential is configured for the selected provider",
71
+ "No usable upstream credential is configured for the selected provider (request {requestId}). Wrong key, right door — check the credential 🔑",
72
72
  { provider_id: providerId },
73
73
  );
74
74
  }