cartethyia 2.8.0 → 2.8.1

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 (348) hide show
  1. package/.env.example +3 -3
  2. package/dist/dashboard/assets/{BannedPage-Cc0vKpMG.js → BannedPage-Dq530awD.js} +1 -1
  3. package/dist/dashboard/assets/BannedPage-Dq530awD.js.gz +0 -0
  4. package/dist/dashboard/assets/{CliToolDetailPage-Bqj2aa1T.js → CliToolDetailPage-DZzaS-7S.js} +1 -1
  5. package/dist/dashboard/assets/CliToolDetailPage-DZzaS-7S.js.gz +0 -0
  6. package/dist/dashboard/assets/{CliToolsPage-CXsaOAO2.js → CliToolsPage-DaZZ3cqP.js} +1 -1
  7. package/dist/dashboard/assets/CliToolsPage-DaZZ3cqP.js.gz +0 -0
  8. package/dist/dashboard/assets/{CombosPage-BeLpJ4ma.js → CombosPage-BlC_xdkX.js} +1 -1
  9. package/dist/dashboard/assets/CombosPage-BlC_xdkX.js.gz +0 -0
  10. package/dist/dashboard/assets/{ConsoleLogPage-DnV0uZ3m.js → ConsoleLogPage-D-rqeVtG.js} +1 -1
  11. package/dist/dashboard/assets/ConsoleLogPage-D-rqeVtG.js.gz +0 -0
  12. package/dist/dashboard/assets/{CustomizationPage-CBxAjo63.js → CustomizationPage-7xT5yg2E.js} +1 -1
  13. package/dist/dashboard/assets/CustomizationPage-7xT5yg2E.js.gz +0 -0
  14. package/dist/dashboard/assets/{HealthEventsModal-B5yEE0k6.js → HealthEventsModal-Clq8E9cW.js} +1 -1
  15. package/dist/dashboard/assets/HealthEventsModal-Clq8E9cW.js.gz +0 -0
  16. package/dist/dashboard/assets/{InjectSystemPromptPanel-BWFLECmz.js → InjectSystemPromptPanel-6Teoq9Se.js} +1 -1
  17. package/dist/dashboard/assets/InjectSystemPromptPanel-6Teoq9Se.js.gz +0 -0
  18. package/dist/dashboard/assets/{LoginPage-CgoPc5B9.js → LoginPage-CZuI0MAR.js} +1 -1
  19. package/dist/dashboard/assets/LoginPage-CZuI0MAR.js.gz +0 -0
  20. package/dist/dashboard/assets/{ModelPicker-D9bFQern.js → ModelPicker-C1hNTlkr.js} +1 -1
  21. package/dist/dashboard/assets/ModelPicker-C1hNTlkr.js.gz +0 -0
  22. package/dist/dashboard/assets/{OverviewPage-B_p4tL8k.js → OverviewPage-0yfhSI1C.js} +1 -1
  23. package/dist/dashboard/assets/OverviewPage-0yfhSI1C.js.gz +0 -0
  24. package/dist/dashboard/assets/ProviderDetailPage-xqTxdQ4c.js +8 -0
  25. package/dist/dashboard/assets/ProviderDetailPage-xqTxdQ4c.js.gz +0 -0
  26. package/dist/dashboard/assets/{ProviderIcon-D_0PiZtg.js → ProviderIcon-Ddhm1InL.js} +1 -1
  27. package/dist/dashboard/assets/ProviderIcon-Ddhm1InL.js.gz +0 -0
  28. package/dist/dashboard/assets/{ProvidersPage-CLI2bJTK.js → ProvidersPage-C3fYuu6i.js} +1 -1
  29. package/dist/dashboard/assets/ProvidersPage-C3fYuu6i.js.gz +0 -0
  30. package/dist/dashboard/assets/ProxyPage-CHtJtZ3u.js +9 -0
  31. package/dist/dashboard/assets/ProxyPage-CHtJtZ3u.js.gz +0 -0
  32. package/dist/dashboard/assets/{QuotaPage-DA-a1iSN.js → QuotaPage-D1RV_2BX.js} +1 -1
  33. package/dist/dashboard/assets/QuotaPage-D1RV_2BX.js.gz +0 -0
  34. package/dist/dashboard/assets/{SettingsPage-CctTga2C.js → SettingsPage-BzZAvYSX.js} +1 -1
  35. package/dist/dashboard/assets/SettingsPage-BzZAvYSX.js.gz +0 -0
  36. package/dist/dashboard/assets/{SetupPage-febLU4r1.js → SetupPage--s-EfEIL.js} +1 -1
  37. package/dist/dashboard/assets/SetupPage--s-EfEIL.js.gz +0 -0
  38. package/dist/dashboard/assets/{StudioPage-M6fvR5Wz.js → StudioPage-BjXFGSK4.js} +1 -1
  39. package/dist/dashboard/assets/StudioPage-BjXFGSK4.js.gz +0 -0
  40. package/dist/dashboard/assets/{UsagePage-nyeuRqz9.js → UsagePage-CP8F8BN7.js} +1 -1
  41. package/dist/dashboard/assets/UsagePage-CP8F8BN7.js.gz +0 -0
  42. package/dist/dashboard/assets/{api-keys-BFOlvrJC.js → api-keys-2hc12Uz2.js} +1 -1
  43. package/dist/dashboard/assets/api-keys-2hc12Uz2.js.gz +0 -0
  44. package/dist/dashboard/assets/app-DGXbNfAR.css +2 -0
  45. package/dist/dashboard/assets/app-DGXbNfAR.css.gz +0 -0
  46. package/dist/dashboard/assets/{assert-CqTLw5e7.js → assert-BMcj1xZE.js} +1 -1
  47. package/dist/dashboard/assets/assert-BMcj1xZE.js.gz +0 -0
  48. package/dist/dashboard/assets/{badge-C83EqLB0.js → badge-DkRUuVfz.js} +1 -1
  49. package/dist/dashboard/assets/badge-DkRUuVfz.js.gz +0 -0
  50. package/dist/dashboard/assets/{banner-D1xn7pIe.js → banner-CDjW841Q.js} +1 -1
  51. package/dist/dashboard/assets/banner-CDjW841Q.js.gz +0 -0
  52. package/dist/dashboard/assets/{card-DU3Lq5Lm.js → card-Bto5UlL9.js} +1 -1
  53. package/dist/dashboard/assets/card-Bto5UlL9.js.gz +0 -0
  54. package/dist/dashboard/assets/{cli-tools-Dw1U_drK.js → cli-tools-DaH6g9hL.js} +1 -1
  55. package/dist/dashboard/assets/cli-tools-DaH6g9hL.js.gz +0 -0
  56. package/dist/dashboard/assets/{clipboard-button-CEL5Sg_J.js → clipboard-button-BYKBCzjk.js} +1 -1
  57. package/dist/dashboard/assets/clipboard-button-BYKBCzjk.js.gz +0 -0
  58. package/dist/dashboard/assets/{dialog-BjFR5UHy.js → dialog-DL0Zftwv.js} +1 -1
  59. package/dist/dashboard/assets/dialog-DL0Zftwv.js.gz +0 -0
  60. package/dist/dashboard/assets/{drawer-HvBW0AjI.js → drawer-Dpx_UXaS.js} +1 -1
  61. package/dist/dashboard/assets/drawer-Dpx_UXaS.js.gz +0 -0
  62. package/dist/dashboard/assets/{entry-CECVELyy.js → entry-BV7-1NoM.js} +1 -1
  63. package/dist/dashboard/assets/entry-BV7-1NoM.js.gz +0 -0
  64. package/dist/dashboard/assets/{entry-DYYofQ7W.js → entry-CGvdNU6r.js} +1 -1
  65. package/dist/dashboard/assets/entry-CGvdNU6r.js.gz +0 -0
  66. package/dist/dashboard/assets/{entry-BSu3gFlt.js → entry-DScKYvZj.js} +3 -3
  67. package/dist/dashboard/assets/entry-DScKYvZj.js.gz +0 -0
  68. package/dist/dashboard/assets/{formatters-DLW4i8zK.js → formatters-BS3DZlZj.js} +1 -1
  69. package/dist/dashboard/assets/formatters-BS3DZlZj.js.gz +0 -0
  70. package/dist/dashboard/assets/{index-1-fCqe64.js → index-SQnvvTWX.js} +2 -2
  71. package/dist/dashboard/assets/index-SQnvvTWX.js.gz +0 -0
  72. package/dist/dashboard/assets/{inline-DGvU2oLK.js → inline-Dl-cuLK0.js} +1 -1
  73. package/dist/dashboard/assets/inline-Dl-cuLK0.js.gz +0 -0
  74. package/dist/dashboard/assets/{input-iNnO1X2K.js → input-Bfx0EzRc.js} +1 -1
  75. package/dist/dashboard/assets/input-Bfx0EzRc.js.gz +0 -0
  76. package/dist/dashboard/assets/{layout-0q8UGypb.js → layout-BxhbQ_JS.js} +1 -1
  77. package/dist/dashboard/assets/layout-BxhbQ_JS.js.gz +0 -0
  78. package/dist/dashboard/assets/{list-row-C0w2bQjJ.js → list-row-D49Y4ptn.js} +1 -1
  79. package/dist/dashboard/assets/list-row-D49Y4ptn.js.gz +0 -0
  80. package/dist/dashboard/assets/{live-M5aJ_xCa.js → live-CbVTdX9V.js} +1 -1
  81. package/dist/dashboard/assets/live-CbVTdX9V.js.gz +0 -0
  82. package/dist/dashboard/assets/{network-BKeQGT3K.js → network-JGvDveof.js} +1 -1
  83. package/dist/dashboard/assets/network-JGvDveof.js.gz +0 -0
  84. package/dist/dashboard/assets/{numbers-CivAk3da.js → numbers-Dw6kiew-.js} +1 -1
  85. package/dist/dashboard/assets/numbers-Dw6kiew-.js.gz +0 -0
  86. package/dist/dashboard/assets/{pagination-CpzvI5ep.js → pagination-iUoI8GeE.js} +1 -1
  87. package/dist/dashboard/assets/pagination-iUoI8GeE.js.gz +0 -0
  88. package/dist/dashboard/assets/providers/sensenova-intl.png +0 -0
  89. package/dist/dashboard/assets/providers/sensenova.png +0 -0
  90. package/dist/dashboard/assets/{routing-BFQX2VBc.js → routing-8jZr-3WE.js} +1 -1
  91. package/dist/dashboard/assets/routing-8jZr-3WE.js.gz +0 -0
  92. package/dist/dashboard/assets/{select-CXbck0jG.js → select-oO1FxMVb.js} +1 -1
  93. package/dist/dashboard/assets/select-oO1FxMVb.js.gz +0 -0
  94. package/dist/dashboard/assets/{settings-C_XvMHh9.js → settings-CLr3kdIx.js} +1 -1
  95. package/dist/dashboard/assets/settings-CLr3kdIx.js.gz +0 -0
  96. package/dist/dashboard/assets/{stack-DggRiCxE.js → stack-Y3pDteZr.js} +1 -1
  97. package/dist/dashboard/assets/stack-Y3pDteZr.js.gz +0 -0
  98. package/dist/dashboard/assets/{state-ClFzfbYp.js → state-CArX_Iak.js} +1 -1
  99. package/dist/dashboard/assets/state-CArX_Iak.js.gz +0 -0
  100. package/dist/dashboard/assets/{switch-CTIFxKqS.js → switch-DwlwmDO-.js} +1 -1
  101. package/dist/dashboard/assets/switch-DwlwmDO-.js.gz +0 -0
  102. package/dist/dashboard/assets/{useMutation-BClG3yWj.js → useMutation-DJ9GYEgf.js} +1 -1
  103. package/dist/dashboard/assets/useMutation-DJ9GYEgf.js.gz +0 -0
  104. package/dist/dashboard/index.html +2 -2
  105. package/dist/dashboard/index.html.gz +0 -0
  106. package/dist/dashboard/precompressed-assets.json +51 -51
  107. package/migrations/full-postgres/0047_add_provider_device_flow_proxy.sql +17 -0
  108. package/migrations/lite-sqlite3/0047_add_provider_device_flow_proxy.sql +19 -0
  109. package/package.json +1 -1
  110. package/src/boot/app.ts +1 -1
  111. package/src/boot/dependencies.ts +4 -6
  112. package/src/config/providers.ts +6 -5
  113. package/src/config/spec.ts +8 -8
  114. package/src/console/backup/nine-router.ts +71 -10
  115. package/src/console/backup/service.ts +8 -1
  116. package/src/console/console-router.ts +1 -1
  117. package/src/console/domain-registration.ts +7 -2
  118. package/src/console/inferhub/client.ts +1 -1
  119. package/src/console/observability/live.ts +2 -2
  120. package/src/console/providers/catalog/contracts.ts +9 -3
  121. package/src/console/providers/catalog/model-operations.ts +2 -3
  122. package/src/console/providers/catalog/provider-operations.ts +1 -1
  123. package/src/console/providers/catalog/routes.ts +2 -2
  124. package/src/console/providers/catalog/store.ts +1914 -1907
  125. package/src/console/providers/detail/routes.ts +1 -0
  126. package/src/console/providers/detail/store.ts +4 -0
  127. package/src/console/providers/oauth/callback-listener.ts +30 -11
  128. package/src/console/providers/oauth/device-egress.ts +123 -0
  129. package/src/console/providers/oauth/routes.ts +123 -16
  130. package/src/console/quota/account-quota-tenant.ts +5 -5
  131. package/src/console/quota/account-test-egress.ts +2 -2
  132. package/src/console/quota/daily-growth-job.ts +1 -1
  133. package/src/console/routing/pools/contracts.ts +1 -1
  134. package/src/console/routing/pools/operations.ts +1 -1
  135. package/src/console/routing/pools/probe-verdict.ts +2 -2
  136. package/src/console/routing/pools/relay-deploy.ts +1 -1
  137. package/src/console/routing/pools/store.ts +4 -4
  138. package/src/network/client.ts +67 -0
  139. package/src/network/egress/bidi-session.ts +322 -0
  140. package/src/network/egress/dial.ts +201 -0
  141. package/src/network/egress/factory.ts +191 -0
  142. package/src/network/{outbound-fetch.ts → egress/fetch.ts} +195 -29
  143. package/src/network/egress/tls-profile.ts +123 -0
  144. package/src/network/egress/ws.ts +175 -0
  145. package/src/network/{types.ts → pool/agent-config.ts} +1 -1
  146. package/src/network/pool/agent.ts +2 -2
  147. package/src/network/{pool-cooldown-store.ts → pool/cooldown-store.ts} +7 -7
  148. package/src/network/{pool-health-machine.ts → pool/health.ts} +205 -6
  149. package/src/network/pool/resolver.ts +10 -849
  150. package/src/network/pool/selector.ts +2 -2
  151. package/src/network/ssrf.ts +1 -1
  152. package/src/network/{retry.ts → util/retry.ts} +1 -1
  153. package/src/observability/telemetry-buffer.ts +1 -1
  154. package/src/persistence/schema.ts +9 -0
  155. package/src/persistence/ttl-cache.ts +1 -1
  156. package/src/protocol/codecs.ts +58 -1
  157. package/src/protocol/request/messages.ts +31 -18
  158. package/src/protocol/response/messages.ts +34 -7
  159. package/src/protocol/translation/quirks.ts +15 -0
  160. package/src/protocol/upstream/send-claude-messages.ts +2 -2
  161. package/src/providers/adapters/compatible-adapter.ts +5 -0
  162. package/src/providers/authentication/oauth-client.ts +40 -6
  163. package/src/providers/authentication/oauth-device-flow.ts +7 -3
  164. package/src/providers/authentication/oauth-flow-store.ts +82 -1
  165. package/src/providers/authentication/oauth-refresh-service.ts +766 -739
  166. package/src/providers/default-registry.ts +76 -0
  167. package/src/providers/discovery/discovery-types.ts +7 -0
  168. package/src/providers/discovery/probe-phases.ts +6 -2
  169. package/src/providers/discovery/probing-service.ts +7 -0
  170. package/src/providers/integrations/antigravity/antigravity-protocol.ts +11 -0
  171. package/src/providers/integrations/buddy/account.ts +969 -0
  172. package/src/providers/integrations/buddy/capabilities.ts +10 -10
  173. package/src/providers/integrations/buddy/{buddy-catalog-shared.ts → catalog.ts} +198 -136
  174. package/src/providers/integrations/buddy/{buddy-chat-shared.ts → chat.ts} +5 -5
  175. package/src/providers/integrations/buddy/codebuddy/index.ts +429 -0
  176. package/src/providers/integrations/buddy/codebuddy/shared.ts +4 -4
  177. package/src/providers/integrations/buddy/{buddy-identity.ts → headers.ts} +138 -140
  178. package/src/providers/integrations/buddy/lifecycle.ts +1018 -0
  179. package/src/providers/integrations/buddy/{buddy-media.ts → media.ts} +1 -1
  180. package/src/providers/integrations/cline/cline-oauth.ts +10 -5
  181. package/src/providers/integrations/cline/cline.ts +3 -4
  182. package/src/providers/integrations/clixad/clixad-oauth.ts +98 -0
  183. package/src/providers/integrations/clixad/clixad-quota.ts +132 -0
  184. package/src/providers/integrations/clixad/provider.ts +145 -0
  185. package/src/providers/integrations/codex/codex-oauth.ts +8 -5
  186. package/src/providers/integrations/cursor/cursor-auth.ts +1 -1
  187. package/src/providers/integrations/cursor/cursor-hardening.ts +279 -0
  188. package/src/providers/integrations/cursor/cursor-http2.ts +605 -6
  189. package/src/providers/integrations/cursor/cursor-shared.ts +1 -1
  190. package/src/providers/integrations/cursor/cursor.ts +485 -70
  191. package/src/providers/integrations/deepai/deepai-catalog.ts +149 -0
  192. package/src/providers/integrations/deepai/deepai-common.ts +145 -0
  193. package/src/providers/integrations/deepai/deepai-oauth.ts +129 -0
  194. package/src/providers/integrations/deepai/deepai-quota.ts +97 -0
  195. package/src/providers/integrations/deepai/provider.ts +42 -0
  196. package/src/providers/integrations/devin/devin-auth.ts +7 -0
  197. package/src/providers/integrations/factory/factory-oauth.ts +16 -7
  198. package/src/providers/integrations/freebuff/freebuff.ts +2 -2
  199. package/src/providers/integrations/freepi/freepi-catalog.ts +5 -0
  200. package/src/providers/integrations/freepi/freepi-discovery.ts +2 -2
  201. package/src/providers/integrations/freepi/freepi-headers.ts +2 -2
  202. package/src/providers/integrations/freepi/freepi-protocol.ts +11 -2
  203. package/src/providers/integrations/freepi/freepi-realm.ts +0 -3
  204. package/src/providers/integrations/freepi/freepi-session.ts +158 -0
  205. package/src/providers/integrations/freepi/freepi.ts +80 -54
  206. package/src/providers/integrations/github/github-discovery.ts +30 -13
  207. package/src/providers/integrations/github/github-oauth.ts +217 -44
  208. package/src/providers/integrations/github/github-quota.ts +22 -5
  209. package/src/providers/integrations/github/github.ts +40 -32
  210. package/src/providers/integrations/grok/grok-oauth.ts +4 -4
  211. package/src/providers/integrations/hermes/hermes-oauth.ts +3 -3
  212. package/src/providers/integrations/hermes/hermes.ts +34 -5
  213. package/src/providers/integrations/kilo/kilo-oauth.ts +4 -4
  214. package/src/providers/integrations/kilo/kilo.ts +1 -1
  215. package/src/providers/integrations/kimi/kimi-oauth.ts +3 -3
  216. package/src/providers/integrations/kiro/kiro-oauth.ts +2 -2
  217. package/src/providers/integrations/muse/muse-oauth.ts +5 -5
  218. package/src/providers/integrations/opencode/fingerprint.ts +1 -1
  219. package/src/providers/integrations/openrouter/provider.ts +1 -1
  220. package/src/providers/integrations/perch/perch-protocol.ts +0 -3
  221. package/src/providers/integrations/perch/perch.ts +3 -3
  222. package/src/providers/integrations/qoder/claim-qoder-credits.ts +1 -1
  223. package/src/providers/integrations/qoder/qoder-adapter.ts +19 -113
  224. package/src/providers/integrations/qoder/qoder-auth.ts +8 -1
  225. package/src/providers/integrations/qoder/qoder-catalog.ts +1 -1
  226. package/src/providers/integrations/qoder/qoder-chat.ts +25 -8
  227. package/src/providers/integrations/qoder/qoder-checkin.ts +1 -1
  228. package/src/providers/integrations/qoder/qoder-cosy.ts +37 -9
  229. package/src/providers/integrations/qoder/qoder-oauth.ts +9 -2
  230. package/src/providers/integrations/qoder/qoder-realm.ts +13 -36
  231. package/src/providers/integrations/qoder/qoder-sse.ts +447 -68
  232. package/src/providers/integrations/qoder/qoder-types.ts +9 -0
  233. package/src/providers/integrations/qoder/qoder.ts +3 -25
  234. package/src/providers/integrations/sensenova/provider.ts +144 -0
  235. package/src/providers/integrations/trae/trae-quota.ts +15 -1
  236. package/src/providers/integrations/vercel/provider.ts +117 -0
  237. package/src/providers/integrations/vercel/vercel-quota.ts +96 -0
  238. package/src/providers/integrations/xai/xai-oauth.ts +5 -5
  239. package/src/providers/integrations/zcode-start-plan/gifts.ts +1 -1
  240. package/src/providers/integrations/zcode-start-plan/oauth.ts +3 -3
  241. package/src/providers/integrations/zro/zro.ts +2 -1
  242. package/src/providers/operations/account-refresh-status.ts +14 -0
  243. package/src/providers/operations/client-versions.ts +95 -30
  244. package/src/providers/operations/oauth-refresh-lead.ts +46 -22
  245. package/src/providers/operations/provider-caches.ts +1 -1
  246. package/src/providers/operations/provider-credential-service.ts +22 -25
  247. package/src/providers/operations/session-resolution.ts +17 -1
  248. package/src/providers/provider-contracts.ts +2 -2
  249. package/src/providers/provider-metadata.ts +30 -0
  250. package/src/providers/quota/quota-contracts.ts +2 -0
  251. package/src/providers/quota/refresh.ts +25 -3
  252. package/src/security/ip-boundary.ts +1 -1
  253. package/src/transport/dispatch/attempt-finalize.ts +1 -1
  254. package/src/transport/dispatch/attempt-loop.ts +3 -3
  255. package/src/transport/dispatch/handler-deps.ts +1 -1
  256. package/src/transport/dispatch/leases.ts +1 -1
  257. package/src/transport/dispatch/retry-policy.ts +1 -1
  258. package/src/transport/dispatch/streaming/finalize.ts +2 -2
  259. package/src/transport/dispatch/streaming/stream.ts +1 -1
  260. package/src/transport/dispatch/upstream.ts +1 -1
  261. package/src/transport/shutdown-notice.ts +1 -1
  262. package/src/workers/buddy-growth-tasks.ts +8 -8
  263. package/src/workers/daily-checkin.ts +4 -4
  264. package/src/workers/quota-refresh-sweep.ts +1 -1
  265. package/src/workers/zcode-gift-claim.ts +1 -1
  266. package/dist/dashboard/assets/BannedPage-Cc0vKpMG.js.gz +0 -0
  267. package/dist/dashboard/assets/CliToolDetailPage-Bqj2aa1T.js.gz +0 -0
  268. package/dist/dashboard/assets/CliToolsPage-CXsaOAO2.js.gz +0 -0
  269. package/dist/dashboard/assets/CombosPage-BeLpJ4ma.js.gz +0 -0
  270. package/dist/dashboard/assets/ConsoleLogPage-DnV0uZ3m.js.gz +0 -0
  271. package/dist/dashboard/assets/CustomizationPage-CBxAjo63.js.gz +0 -0
  272. package/dist/dashboard/assets/HealthEventsModal-B5yEE0k6.js.gz +0 -0
  273. package/dist/dashboard/assets/InjectSystemPromptPanel-BWFLECmz.js.gz +0 -0
  274. package/dist/dashboard/assets/LoginPage-CgoPc5B9.js.gz +0 -0
  275. package/dist/dashboard/assets/ModelPicker-D9bFQern.js.gz +0 -0
  276. package/dist/dashboard/assets/OverviewPage-B_p4tL8k.js.gz +0 -0
  277. package/dist/dashboard/assets/ProviderDetailPage-rs5E2rxv.js +0 -8
  278. package/dist/dashboard/assets/ProviderDetailPage-rs5E2rxv.js.gz +0 -0
  279. package/dist/dashboard/assets/ProviderIcon-D_0PiZtg.js.gz +0 -0
  280. package/dist/dashboard/assets/ProvidersPage-CLI2bJTK.js.gz +0 -0
  281. package/dist/dashboard/assets/ProxyPage-BwNC_-Cx.js +0 -9
  282. package/dist/dashboard/assets/ProxyPage-BwNC_-Cx.js.gz +0 -0
  283. package/dist/dashboard/assets/QuotaPage-DA-a1iSN.js.gz +0 -0
  284. package/dist/dashboard/assets/SettingsPage-CctTga2C.js.gz +0 -0
  285. package/dist/dashboard/assets/SetupPage-febLU4r1.js.gz +0 -0
  286. package/dist/dashboard/assets/StudioPage-M6fvR5Wz.js.gz +0 -0
  287. package/dist/dashboard/assets/UsagePage-nyeuRqz9.js.gz +0 -0
  288. package/dist/dashboard/assets/api-keys-BFOlvrJC.js.gz +0 -0
  289. package/dist/dashboard/assets/app-Dx2QgsNj.css +0 -2
  290. package/dist/dashboard/assets/app-Dx2QgsNj.css.gz +0 -0
  291. package/dist/dashboard/assets/assert-CqTLw5e7.js.gz +0 -0
  292. package/dist/dashboard/assets/badge-C83EqLB0.js.gz +0 -0
  293. package/dist/dashboard/assets/banner-D1xn7pIe.js.gz +0 -0
  294. package/dist/dashboard/assets/card-DU3Lq5Lm.js.gz +0 -0
  295. package/dist/dashboard/assets/cli-tools-Dw1U_drK.js.gz +0 -0
  296. package/dist/dashboard/assets/clipboard-button-CEL5Sg_J.js.gz +0 -0
  297. package/dist/dashboard/assets/dialog-BjFR5UHy.js.gz +0 -0
  298. package/dist/dashboard/assets/drawer-HvBW0AjI.js.gz +0 -0
  299. package/dist/dashboard/assets/entry-BSu3gFlt.js.gz +0 -0
  300. package/dist/dashboard/assets/entry-CECVELyy.js.gz +0 -0
  301. package/dist/dashboard/assets/entry-DYYofQ7W.js.gz +0 -0
  302. package/dist/dashboard/assets/formatters-DLW4i8zK.js.gz +0 -0
  303. package/dist/dashboard/assets/index-1-fCqe64.js.gz +0 -0
  304. package/dist/dashboard/assets/inline-DGvU2oLK.js.gz +0 -0
  305. package/dist/dashboard/assets/input-iNnO1X2K.js.gz +0 -0
  306. package/dist/dashboard/assets/layout-0q8UGypb.js.gz +0 -0
  307. package/dist/dashboard/assets/list-row-C0w2bQjJ.js.gz +0 -0
  308. package/dist/dashboard/assets/live-M5aJ_xCa.js.gz +0 -0
  309. package/dist/dashboard/assets/network-BKeQGT3K.js.gz +0 -0
  310. package/dist/dashboard/assets/numbers-CivAk3da.js.gz +0 -0
  311. package/dist/dashboard/assets/pagination-CpzvI5ep.js.gz +0 -0
  312. package/dist/dashboard/assets/routing-BFQX2VBc.js.gz +0 -0
  313. package/dist/dashboard/assets/select-CXbck0jG.js.gz +0 -0
  314. package/dist/dashboard/assets/settings-C_XvMHh9.js.gz +0 -0
  315. package/dist/dashboard/assets/stack-DggRiCxE.js.gz +0 -0
  316. package/dist/dashboard/assets/state-ClFzfbYp.js.gz +0 -0
  317. package/dist/dashboard/assets/switch-CTIFxKqS.js.gz +0 -0
  318. package/dist/dashboard/assets/useMutation-BClG3yWj.js.gz +0 -0
  319. package/src/console/providers/catalog/discover-constants.ts +0 -6
  320. package/src/network/pool-health.ts +0 -198
  321. package/src/providers/integrations/buddy/buddy-checkin.ts +0 -521
  322. package/src/providers/integrations/buddy/buddy-cli-headers.ts +0 -76
  323. package/src/providers/integrations/buddy/buddy-conversation-headers.ts +0 -130
  324. package/src/providers/integrations/buddy/buddy-discovery-shared.ts +0 -234
  325. package/src/providers/integrations/buddy/buddy-growth-shared.ts +0 -496
  326. package/src/providers/integrations/buddy/buddy-oauth-shared.ts +0 -341
  327. package/src/providers/integrations/buddy/buddy-quota-shared.ts +0 -417
  328. package/src/providers/integrations/buddy/buddy-server-tools.ts +0 -240
  329. package/src/providers/integrations/buddy/buddy-usage-shared.ts +0 -221
  330. package/src/providers/integrations/buddy/buddy-usage-store.ts +0 -181
  331. package/src/providers/integrations/buddy/codebuddy/cn-catalog.ts +0 -37
  332. package/src/providers/integrations/buddy/codebuddy/cn-provider.ts +0 -188
  333. package/src/providers/integrations/buddy/codebuddy/oauth.ts +0 -47
  334. package/src/providers/integrations/buddy/codebuddy/provider.ts +0 -179
  335. package/src/providers/integrations/buddy/codebuddy/quota.ts +0 -154
  336. package/src/providers/integrations/qoder/chat-proto.ts +0 -328
  337. package/src/providers/integrations/qoder/qoder-grpc.ts +0 -870
  338. package/src/providers/integrations/qoder/qoder-ide.ts +0 -481
  339. /package/dist/dashboard/assets/{app-DIXLjojg.js → app-C0gTXEoN.js} +0 -0
  340. /package/dist/dashboard/assets/{app-DIXLjojg.js.gz → app-C0gTXEoN.js.gz} +0 -0
  341. /package/src/network/{http2-bidi.ts → bidi.ts} +0 -0
  342. /package/src/network/pool/{byte-accounting.ts → accounting.ts} +0 -0
  343. /package/src/network/pool/{pool-scripts.ts → scripts.ts} +0 -0
  344. /package/src/network/pool/{pool-state.ts → state.ts} +0 -0
  345. /package/src/network/{backoff.ts → util/backoff.ts} +0 -0
  346. /package/src/network/{deduplication.ts → util/dedupe.ts} +0 -0
  347. /package/src/network/{ip-utils.ts → util/ip.ts} +0 -0
  348. /package/src/network/{rate-limit-verdict.ts → verdict.ts} +0 -0
@@ -0,0 +1,144 @@
1
+ // SenseNova providers (CN + Intl) — bearer-authenticated OpenAI-compatible
2
+ // chat + image generation.
3
+ //
4
+ // SenseNova API keys are per-site: keys created on platform.sensenova.cn are
5
+ // rejected by token.sensenova.ai with 401 {"error":{"code":16,"message":"Forbidden"}}
6
+ // (and vice versa), so each deployment is its own provider with its own host
7
+ // and catalog — the same split as CodeBuddy cb/cbcn.
8
+ //
9
+ // Wire facts:
10
+ // - Chat is OpenAI-compatible `/v1/chat/completions` with Bearer auth.
11
+ // - Image generation is `/v1/images/generations` (OpenAI-shaped `{model,
12
+ // prompt, n, size, output_format, response_format, watermark,
13
+ // prompt_extend}`), answering `{data: [{url|b64_json}]}`.
14
+ // - Thinking streams under `reasoning` (delta + message) rather than
15
+ // `reasoning_content`; the shared compatible adapter's chat decoder maps
16
+ // it onto the canonical reasoning event.
17
+
18
+ import { defineModel, type ModelDefinition } from "../../provider-model-definition";
19
+ import type { ApiKeyProviderSpec } from "../../configured-provider";
20
+ import {
21
+ fetchOpenAICompatibleModels,
22
+ } from "../../discovery/openai-model-discovery";
23
+
24
+ /** Chat path below each realm's `/v1` base root. */
25
+ const SENSENOVA_CHAT_PATH = "/chat/completions";
26
+ /** Image-generation path below each realm's `/v1` base root. */
27
+ const SENSENOVA_IMAGE_PATH = "/images/generations";
28
+
29
+ export const SENSENOVA_PROVIDER_ID = "sensenova" as const;
30
+ export const SENSENOVA_INTL_PROVIDER_ID = "sensenova-intl" as const;
31
+
32
+ function sensenovaSpec(providerId: string): ApiKeyProviderSpec {
33
+ return {
34
+ provider_id: providerId as ApiKeyProviderSpec["provider_id"],
35
+ endpoint_paths_by_wire_family: { chat: SENSENOVA_CHAT_PATH },
36
+ };
37
+ }
38
+
39
+ /** SenseNova CN adapter spec. */
40
+ export const SENSENOVA_SPEC: ApiKeyProviderSpec = sensenovaSpec(SENSENOVA_PROVIDER_ID);
41
+
42
+ /** SenseNova Intl adapter spec. */
43
+ export const SENSENOVA_INTL_SPEC: ApiKeyProviderSpec = sensenovaSpec(SENSENOVA_INTL_PROVIDER_ID);
44
+
45
+ /**
46
+ * Shared chat roster (both realms expose the same model families; the intl
47
+ * deployment advertises fewer static rows, and live discovery fills both).
48
+ *
49
+ * Image rows are media-generation rows (serviceKind `imageGeneration`), not
50
+ * chat rows — they answer `/v1/images/generations` and are routed by the
51
+ * native image route.
52
+ */
53
+ const SENSENOVA_CHAT_MODELS: readonly string[] = [
54
+ "sensenova-6.8-flash",
55
+ "sensenova-6.8-flash-lite",
56
+ "sensenova-6.7-flash-lite",
57
+ "deepseek-flash",
58
+ "deepseek-v4-flash",
59
+ "glm-5.3",
60
+ "glm-5.2",
61
+ "kimi-k3",
62
+ ];
63
+
64
+ function makeChatModels(providerId: string): ModelDefinition[] {
65
+ return SENSENOVA_CHAT_MODELS.map((id) =>
66
+ defineModel({
67
+ id,
68
+ providerId,
69
+ wireFamily: "chat",
70
+ endpoint: SENSENOVA_CHAT_PATH,
71
+ ctx: 200_000,
72
+ out: 32_000,
73
+ reasoning: true,
74
+ toolCall: true,
75
+ }),
76
+ );
77
+ }
78
+
79
+ function makeImageModels(providerId: string): ModelDefinition[] {
80
+ return [
81
+ defineModel({
82
+ id: "sensenova-u1.5-lite",
83
+ providerId,
84
+ wireFamily: "chat",
85
+ serviceKind: "imageGeneration",
86
+ endpoint: SENSENOVA_IMAGE_PATH,
87
+ ctx: null,
88
+ out: null,
89
+ toolCall: false,
90
+ output: ["image"],
91
+ }),
92
+ defineModel({
93
+ id: "sensenova-u1.5-fast",
94
+ providerId,
95
+ wireFamily: "chat",
96
+ serviceKind: "imageGeneration",
97
+ endpoint: SENSENOVA_IMAGE_PATH,
98
+ ctx: null,
99
+ out: null,
100
+ toolCall: false,
101
+ output: ["image"],
102
+ }),
103
+ defineModel({
104
+ id: "sensenova-u1-fast",
105
+ providerId,
106
+ wireFamily: "chat",
107
+ serviceKind: "imageGeneration",
108
+ endpoint: SENSENOVA_IMAGE_PATH,
109
+ ctx: null,
110
+ out: null,
111
+ toolCall: false,
112
+ output: ["image"],
113
+ }),
114
+ ];
115
+ }
116
+
117
+ /** Full CN catalog: chat + image rows. */
118
+ export const SENSENOVA_MODELS: readonly ModelDefinition[] = [
119
+ ...makeChatModels(SENSENOVA_PROVIDER_ID),
120
+ ...makeImageModels(SENSENOVA_PROVIDER_ID),
121
+ ];
122
+
123
+ /** Full Intl catalog: chat + image rows. */
124
+ export const SENSENOVA_INTL_MODELS: readonly ModelDefinition[] = [
125
+ ...makeChatModels(SENSENOVA_INTL_PROVIDER_ID),
126
+ ...makeImageModels(SENSENOVA_INTL_PROVIDER_ID),
127
+ ];
128
+
129
+ /** Live `/v1/models` listing for one realm. */
130
+ export function discoverSensenovaModels(options: {
131
+ readonly baseUrl: string;
132
+ readonly credential?: string;
133
+ readonly fetcher?: typeof fetch;
134
+ readonly providerId: string;
135
+ }): Promise<readonly ModelDefinition[] | null> {
136
+ return fetchOpenAICompatibleModels({
137
+ baseUrl: options.baseUrl,
138
+ providerId: options.providerId,
139
+ ...(typeof options.credential === "string" && options.credential.trim().length > 0
140
+ ? { headers: { authorization: `Bearer ${options.credential}` } }
141
+ : {}),
142
+ ...(options.fetcher === undefined ? {} : { fetcher: options.fetcher }),
143
+ });
144
+ }
@@ -317,6 +317,13 @@ function logTraeQuotaMiss(errors: readonly string[]): void {
317
317
  log.warn(`[quota] trae surfaces unavailable: ${errors.join("; ")}`);
318
318
  }
319
319
 
320
+ /** Only this exact response from both quota surfaces on both realms is terminal auth evidence. */
321
+ function isTraeAuthInvalidation(errors: readonly string[]): boolean {
322
+ return errors.length === 4 && errors.every((error) =>
323
+ /HTTP 401\s+\{[^}]*"code"\s*:\s*1001\b/i.test(error),
324
+ );
325
+ }
326
+
320
327
  /**
321
328
  * Reads one Trae account's quota.
322
329
  *
@@ -421,7 +428,14 @@ export async function fetchTraeQuota(
421
428
  // The detail is kept for the log instead, where it cannot be read as a
422
429
  // balance.
423
430
  logTraeQuotaMiss(errors);
424
- return { source: "trae", plan: null, windows: [], error: null };
431
+ const authInvalidated = isTraeAuthInvalidation(errors);
432
+ return {
433
+ source: "trae",
434
+ plan: null,
435
+ windows: [],
436
+ error: authInvalidated ? errors.join("; ") : null,
437
+ ...(authInvalidated ? { authInvalidated: true } : {}),
438
+ };
425
439
  }
426
440
  return {
427
441
  source: "trae",
@@ -0,0 +1,117 @@
1
+ // Vercel AI Gateway provider — TypeScript, Vercel AI Gateway, Bun gateway.
2
+ //
3
+ // Vercel AI Gateway is an OpenAI-compatible routing front for 500+ models
4
+ // across providers. The API key (`vck_...`) is bearer-authenticated against
5
+ // `https://ai-gateway.vercel.sh/v1`.
6
+ //
7
+ // Verified live 2026-11-11 with a free-tier key:
8
+ // - GET /v1/models → 200, 506 rows (OpenRouter-shaped list)
9
+ // - POST /v1/chat/completions → 200 for nvidia/nemotron-3.5-lightning-free
10
+ // - POST /v1/chat/completions → 403 `no_providers_available` for the other
11
+ // zero-priced rows (reflection/beam*, humansand/ampersand-v0.1,
12
+ // stealth/glyph-cluster) — listed at $0 but gated behind a paid/Pro plan.
13
+ // - GET /v1/credits → { balance, token_pass, total_used }
14
+ //
15
+ // The static roster below carries the five $0-priced rows, so the free section
16
+ // is populated before any fetch. Four of them are marked `proOnly` because the
17
+ // upstream refuses them on a free plan (verified live); discovery still finds
18
+ // them and the badge tracks the upstream's own listing.
19
+
20
+ import type { ApiKeyProviderSpec } from "../../configured-provider";
21
+ import { fetchOpenAICompatibleModels } from "../../discovery/openai-model-discovery";
22
+ import { defineModel, type ModelDefinition } from "../../provider-model-definition";
23
+
24
+ const VERCEL_PROVIDER_ID = "vercel" as const;
25
+
26
+ /** Chat path below the `/v1` base root. */
27
+ const VERCEL_CHAT_PATH = "/chat/completions";
28
+
29
+ /**
30
+ * Vercel AI Gateway adapter spec: plain bearer, OpenAI-compatible chat.
31
+ *
32
+ * The gateway accepts standard OpenAI fields (`max_tokens`, `temperature`,
33
+ * `stop`, `tools`, `tool_choice`, `reasoning`, `response_format`, …) and
34
+ * reports usage/cost in extended `provider_metadata`. No bespoke headers are
35
+ * required beyond the bearer, so the generic API-key adapter serves it.
36
+ */
37
+ export const VERCEL_SPEC: ApiKeyProviderSpec = {
38
+ provider_id: VERCEL_PROVIDER_ID,
39
+ endpoint_paths_by_wire_family: {},
40
+ };
41
+
42
+ /**
43
+ * Default rows for the Vercel AI Gateway provider (2026-11-11).
44
+ *
45
+ * Verified live with a free-tier key: only
46
+ * `nvidia/nemotron-3.5-lightning-free` is served on a free plan; the other
47
+ * zero-priced listing rows are refused with 403 `no_providers_available`, so
48
+ * they are not seeded. The two billed rows (`meituan/longcat-2.5`,
49
+ * `inception/mercury-3`) are the operator's picks and carry the capabilities
50
+ * the upstream itself declares — LongCat is the vision-capable coding row,
51
+ * Mercury 3 is the diffusion row. Everything else comes from live discovery.
52
+ */
53
+ export const VERCEL_DEFAULT_MODELS: readonly ModelDefinition[] = [
54
+ defineModel({
55
+ id: "nvidia/nemotron-3.5-lightning-free",
56
+ wireFamily: "chat",
57
+ endpoint: VERCEL_CHAT_PATH,
58
+ ctx: 1_000_000,
59
+ out: 65_000,
60
+ reasoning: true,
61
+ toolCall: true,
62
+ free: true,
63
+ freeTier: true,
64
+ }),
65
+ defineModel({
66
+ id: "meituan/longcat-2.5",
67
+ wireFamily: "chat",
68
+ endpoint: VERCEL_CHAT_PATH,
69
+ ctx: 1_048_576,
70
+ out: 131_072,
71
+ vision: true,
72
+ reasoning: true,
73
+ toolCall: true,
74
+ }),
75
+ defineModel({
76
+ id: "inception/mercury-3",
77
+ wireFamily: "chat",
78
+ endpoint: VERCEL_CHAT_PATH,
79
+ ctx: 1_000_000,
80
+ out: 131_072,
81
+ reasoning: true,
82
+ toolCall: true,
83
+ }),
84
+ ];
85
+
86
+ /**
87
+ * Live `/v1/models` listing.
88
+ *
89
+ * The listing requires the API key, so the credential is forwarded when
90
+ * present; an unauthenticated fetch fails cleanly and the static roster stays
91
+ * authoritative. Every fetched row is kept as the shared reader resolved it
92
+ * (limits/modalities/reasoning/tool flags come from the upstream's own JSON).
93
+ *
94
+ * A fetched id the static roster already knows keeps the roster row whole, not
95
+ * the listing's fields: the upstream does not publish `reasoning`/`tool_call`
96
+ * keys or tier flags, so the shared reader would overwrite the roster's
97
+ * reasoning/tool/freeTier/proOnly statements with its conservative defaults.
98
+ * The roster is the canonical metadata for the models we know; the listing only
99
+ * adds genuinely new ids.
100
+ */
101
+ export async function discoverVercelModels(options: {
102
+ readonly baseUrl: string;
103
+ readonly credential?: string;
104
+ readonly fetcher?: typeof fetch;
105
+ }): Promise<readonly ModelDefinition[] | null> {
106
+ const listed = await fetchOpenAICompatibleModels({
107
+ baseUrl: options.baseUrl,
108
+ providerId: "vercel",
109
+ ...(typeof options.credential === "string" && options.credential.trim().length > 0
110
+ ? { headers: { authorization: `Bearer ${options.credential}` } }
111
+ : {}),
112
+ ...(options.fetcher === undefined ? {} : { fetcher: options.fetcher }),
113
+ });
114
+ if (listed === null) return null;
115
+ const known = new Map(VERCEL_DEFAULT_MODELS.map((model) => [model.modelId, model]));
116
+ return listed.map((model) => known.get(model.modelId) ?? model);
117
+ }
@@ -0,0 +1,96 @@
1
+ // Vercel AI Gateway quota collector.
2
+ //
3
+ // Reads `GET /v1/credits` (Bearer) — the gateway's credit surface. It returns
4
+ // a remaining balance (`balance`, USD string), an optional `token_pass`, and
5
+ // the total spend (`total_used`, USD string). No ceiling is reported (balance
6
+ // is remaining, not a limit), so the window carries absolute remaining/used
7
+ // values and no percent — the pooled credit sum still works. Never throws: a
8
+ // failed read returns a null-result so the router treats it as unknown rather
9
+ // than dead.
10
+
11
+ import type {
12
+ FetchLike,
13
+ ProviderQuotaResult,
14
+ ProviderQuotaWindow,
15
+ } from "../../quota/quota-contracts";
16
+ import {
17
+ cleanError,
18
+ number,
19
+ unsupportedQuota,
20
+ } from "../../quota/quota-contracts";
21
+ import type { QuotaCollectionContext } from "../../quota/quota-support";
22
+
23
+ const VERCEL_CREDITS_URL = "https://ai-gateway.vercel.sh/v1/credits";
24
+
25
+ function readCredits(
26
+ credential: string,
27
+ fetcher: FetchLike,
28
+ ): Promise<Record<string, unknown> | null> {
29
+ return fetcher(VERCEL_CREDITS_URL, {
30
+ method: "GET",
31
+ headers: {
32
+ authorization: `Bearer ${credential}`,
33
+ accept: "application/json",
34
+ },
35
+ signal: AbortSignal.timeout(15_000),
36
+ })
37
+ .then(async (res) => {
38
+ if (!res.ok) return null;
39
+ try {
40
+ return (await res.json()) as Record<string, unknown>;
41
+ } catch {
42
+ return null;
43
+ }
44
+ })
45
+ .catch(() => null);
46
+ }
47
+
48
+ /** Reads the Vercel AI Gateway credit balance and returns a normalized result. */
49
+ export async function fetchVercelQuota(
50
+ credential: string,
51
+ fetcher: FetchLike,
52
+ _context?: QuotaCollectionContext,
53
+ ): Promise<ProviderQuotaResult> {
54
+ try {
55
+ const credits = await readCredits(credential, fetcher);
56
+ if (!credits) {
57
+ return unsupportedQuota("vercel");
58
+ }
59
+ const balance = number(credits["balance"]);
60
+ const totalUsed = number(credits["total_used"]);
61
+ if (balance === null && totalUsed === null) {
62
+ return {
63
+ source: "vercel",
64
+ plan: null,
65
+ windows: [],
66
+ error: "Vercel AI Gateway /v1/credits returned no usable numbers",
67
+ };
68
+ }
69
+ const windows: ProviderQuotaWindow[] = [
70
+ {
71
+ kind: "credits",
72
+ label: "AI Gateway credits",
73
+ accounting: "pooled",
74
+ usedPercent: null,
75
+ remainingPercent: null,
76
+ resetsAt: null,
77
+ used: totalUsed,
78
+ limit: null,
79
+ remaining: balance,
80
+ },
81
+ ];
82
+ return {
83
+ source: "vercel",
84
+ plan: null,
85
+ windows,
86
+ error: null,
87
+ };
88
+ } catch (error) {
89
+ return {
90
+ source: "vercel",
91
+ plan: null,
92
+ windows: [],
93
+ error: cleanError(error),
94
+ };
95
+ }
96
+ }
@@ -135,8 +135,8 @@ export class XaiOAuthClient extends OAuthDeviceFlow {
135
135
  protected override readonly tokenUrl = XAI_TOKEN_URL;
136
136
  protected override readonly scopes = XAI_SCOPE;
137
137
 
138
- override async startDeviceAuth(_context?: OAuthDeviceFlowContext): Promise<OAuthDeviceStartResult> {
139
- const response = await this.fetchFn(XAI_DEVICE_URL, {
138
+ override async startDeviceAuth(context?: OAuthDeviceFlowContext): Promise<OAuthDeviceStartResult> {
139
+ const response = await this.fetchFor(context)(XAI_DEVICE_URL, {
140
140
  method: "POST",
141
141
  headers: formHeaders(),
142
142
  body: new URLSearchParams({ client_id: XAI_CLIENT_ID, scope: XAI_SCOPE }),
@@ -157,9 +157,9 @@ export class XaiOAuthClient extends OAuthDeviceFlow {
157
157
 
158
158
  override async pollDeviceAuth(
159
159
  deviceAuthId: string,
160
- _context?: OAuthDeviceFlowContext,
160
+ context?: OAuthDeviceFlowContext,
161
161
  ): Promise<OAuthDevicePollResult> {
162
- const response = await this.fetchFn(XAI_TOKEN_URL, {
162
+ const response = await this.fetchFor(context)(XAI_TOKEN_URL, {
163
163
  method: "POST",
164
164
  headers: formHeaders(),
165
165
  body: new URLSearchParams({
@@ -203,7 +203,7 @@ export class XaiOAuthClient extends OAuthDeviceFlow {
203
203
  };
204
204
  }
205
205
  const result = this.parseTokenResponse(payload);
206
- const identity = await fetchXaiIdentity(result.access, this.fetchFn);
206
+ const identity = await fetchXaiIdentity(result.access, this.fetchFor(context));
207
207
  const authState = xaiAuthState(identity);
208
208
  return {
209
209
  status: "complete",
@@ -22,7 +22,7 @@
22
22
  import { createHash } from "node:crypto";
23
23
  import { sleep } from "../../../shared/sleep";
24
24
  import { asRecord, asTrimmedString } from "../../../shared/coerce";
25
- import type { ValidatedFetch } from "../../../network/outbound-fetch";
25
+ import type { ValidatedFetch } from "../../../network/egress/fetch";
26
26
 
27
27
  const ZCODE = "https://zcode.z.ai";
28
28
  const BILLING = `${ZCODE}/api/v1/zcode-plan/billing`;
@@ -62,10 +62,10 @@ export class ZcodeStartPlanOAuthClient extends OAuthDeviceFlow {
62
62
  }
63
63
 
64
64
  override async startDeviceAuth(
65
- _context?: OAuthDeviceFlowContext,
65
+ context?: OAuthDeviceFlowContext,
66
66
  ): Promise<OAuthDeviceStartResult & { providerState: string }> {
67
67
  const pollToken = crypto.randomBytes(32).toString("hex");
68
- const response = await this.fetchFn(INIT_URL, {
68
+ const response = await this.fetchFor(context)(INIT_URL, {
69
69
  method: "POST",
70
70
  headers: { ...JSON_HEADERS, authorization: `Bearer ${pollToken}` },
71
71
  body: JSON.stringify({ provider: "zai" }),
@@ -95,7 +95,7 @@ export class ZcodeStartPlanOAuthClient extends OAuthDeviceFlow {
95
95
  if (!pollToken) {
96
96
  return { status: "failed", reason: "sign-in session lost its poll token" };
97
97
  }
98
- const response = await this.fetchFn(`${POLL_BASE}/${encodeURIComponent(deviceAuthId)}`, {
98
+ const response = await this.fetchFor(context)(`${POLL_BASE}/${encodeURIComponent(deviceAuthId)}`, {
99
99
  headers: { ...JSON_HEADERS, authorization: `Bearer ${pollToken}` },
100
100
  });
101
101
  if (response.status === 404) {
@@ -45,7 +45,6 @@ const ZRO_MODELS_RAW: readonly {
45
45
  }[] = [
46
46
  { id: "deepseek-v4-flash-0731", contextLimit: 1_048_576, outputLimit: 64_000 },
47
47
  { id: "deepseek-v4.1-flash", contextLimit: 1_048_576, outputLimit: 64_000 },
48
- { id: "glm-5.3-flash", contextLimit: 1_048_576, outputLimit: 64_000 },
49
48
  ] as const;
50
49
 
51
50
  export const ZRO_MODELS: readonly ModelDefinition[] = ZRO_MODELS_RAW.map((row) =>
@@ -79,6 +78,8 @@ export const ZRO_MODELS: readonly ModelDefinition[] = ZRO_MODELS_RAW.map((row) =
79
78
  */
80
79
  export function zroModelDiscovery(): () => Promise<ProviderModelDiscovery> {
81
80
  return async () => async (input) => {
81
+ // dynamic-import: cycle-break — model discovery is only imported at call time,
82
+ // so a provider that never runs discovery never pays for the OpenAI discovery graph.
82
83
  const { fetchOpenAICompatibleModels } = await import("../../discovery/openai-model-discovery");
83
84
  const base = input.baseUrl.replace(/\/+$/, "");
84
85
  const models = await fetchOpenAICompatibleModels({
@@ -21,6 +21,7 @@ export type RefreshAccountStatusCode =
21
21
  | "not_oauth"
22
22
  | "static_token"
23
23
  | "no_refresher"
24
+ | "auth_invalidated"
24
25
  | "refresh_failed";
25
26
 
26
27
  export interface RefreshAccountStatusOutcome {
@@ -54,6 +55,7 @@ interface LoadedAccount {
54
55
  readonly credentialKind: string;
55
56
  readonly status: string;
56
57
  readonly staticToken: boolean;
58
+ readonly lastErrorCategory: string | null;
57
59
  readonly refreshCiphertext: Buffer | null;
58
60
  readonly expiresAt: Date | null;
59
61
  }
@@ -64,6 +66,7 @@ async function loadAccount(
64
66
  ): Promise<LoadedAccount | undefined> {
65
67
  const loaded = await loadAccountWithOauthState(db, accountId, {
66
68
  includeStatus: true,
69
+ includeLastErrorCategory: true,
67
70
  includeExpiry: true,
68
71
  });
69
72
  if (!loaded || loaded.status === undefined || loaded.expiresAt === undefined) return undefined;
@@ -73,6 +76,7 @@ async function loadAccount(
73
76
  credentialKind: loaded.credentialKind,
74
77
  status: loaded.status,
75
78
  staticToken: loaded.staticToken,
79
+ lastErrorCategory: loaded.lastErrorCategory ?? null,
76
80
  refreshCiphertext: loaded.refreshCiphertext,
77
81
  expiresAt: loaded.expiresAt,
78
82
  };
@@ -137,6 +141,16 @@ export async function refreshAccountStatus(
137
141
  }
138
142
  const statusBefore = account.status;
139
143
 
144
+ if (account.status === "disabled" && account.lastErrorCategory === "auth_invalidated") {
145
+ return {
146
+ ok: false,
147
+ tokenRefreshed: false,
148
+ statusAfter: statusBefore,
149
+ code: "auth_invalidated",
150
+ error: "auth_invalidated",
151
+ category: "This account was rejected by the provider and requires re-authentication; automatic refresh is disabled.",
152
+ };
153
+ }
140
154
  if (account.credentialKind !== "oauth") {
141
155
  return {
142
156
  ok: false,