cartethyia 2.7.9 → 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 (496) hide show
  1. package/.env.example +6 -5
  2. package/dist/dashboard/assets/{BannedPage-BxoG1MrL.js → BannedPage-Dq530awD.js} +1 -1
  3. package/dist/dashboard/assets/BannedPage-Dq530awD.js.gz +0 -0
  4. package/dist/dashboard/assets/{CliToolDetailPage-ixwgKk53.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-BmttNV3U.js → CliToolsPage-DaZZ3cqP.js} +1 -1
  7. package/dist/dashboard/assets/CliToolsPage-DaZZ3cqP.js.gz +0 -0
  8. package/dist/dashboard/assets/{CombosPage-BE3qwwfC.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-CRZdmMIX.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-Dv_FJ6Er.js → CustomizationPage-7xT5yg2E.js} +1 -1
  13. package/dist/dashboard/assets/CustomizationPage-7xT5yg2E.js.gz +0 -0
  14. package/dist/dashboard/assets/HealthEventsModal-Clq8E9cW.js +1 -0
  15. package/dist/dashboard/assets/HealthEventsModal-Clq8E9cW.js.gz +0 -0
  16. package/dist/dashboard/assets/{InjectSystemPromptPanel-BOoO8rpz.js → InjectSystemPromptPanel-6Teoq9Se.js} +2 -2
  17. package/dist/dashboard/assets/InjectSystemPromptPanel-6Teoq9Se.js.gz +0 -0
  18. package/dist/dashboard/assets/{LoginPage-ChJuNNVN.js → LoginPage-CZuI0MAR.js} +1 -1
  19. package/dist/dashboard/assets/LoginPage-CZuI0MAR.js.gz +0 -0
  20. package/dist/dashboard/assets/{ModelPicker-BZAJtPg1.js → ModelPicker-C1hNTlkr.js} +1 -1
  21. package/dist/dashboard/assets/ModelPicker-C1hNTlkr.js.gz +0 -0
  22. package/dist/dashboard/assets/{OverviewPage-BE40s3u5.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-CQ6bPUf_.js → ProviderIcon-Ddhm1InL.js} +1 -1
  27. package/dist/dashboard/assets/ProviderIcon-Ddhm1InL.js.gz +0 -0
  28. package/dist/dashboard/assets/ProvidersPage-C3fYuu6i.js +1 -0
  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-D1RV_2BX.js +1 -0
  33. package/dist/dashboard/assets/QuotaPage-D1RV_2BX.js.gz +0 -0
  34. package/dist/dashboard/assets/{SettingsPage-C5HPWqEv.js → SettingsPage-BzZAvYSX.js} +1 -1
  35. package/dist/dashboard/assets/SettingsPage-BzZAvYSX.js.gz +0 -0
  36. package/dist/dashboard/assets/{SetupPage-CFNcATJJ.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-YoD4tY5l.js → StudioPage-BjXFGSK4.js} +1 -1
  39. package/dist/dashboard/assets/StudioPage-BjXFGSK4.js.gz +0 -0
  40. package/dist/dashboard/assets/{UsagePage-DxjaAhb_.js → UsagePage-CP8F8BN7.js} +1 -1
  41. package/dist/dashboard/assets/UsagePage-CP8F8BN7.js.gz +0 -0
  42. package/dist/dashboard/assets/{api-keys-BTlkq_Uk.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-BMcj1xZE.js +1 -0
  47. package/dist/dashboard/assets/assert-BMcj1xZE.js.gz +0 -0
  48. package/dist/dashboard/assets/{badge-DdbT_7Am.js → badge-DkRUuVfz.js} +1 -1
  49. package/dist/dashboard/assets/badge-DkRUuVfz.js.gz +0 -0
  50. package/dist/dashboard/assets/{banner-CsGN-nbm.js → banner-CDjW841Q.js} +1 -1
  51. package/dist/dashboard/assets/banner-CDjW841Q.js.gz +0 -0
  52. package/dist/dashboard/assets/{card-mgZ_TLLZ.js → card-Bto5UlL9.js} +1 -1
  53. package/dist/dashboard/assets/card-Bto5UlL9.js.gz +0 -0
  54. package/dist/dashboard/assets/{cli-tools-A1-v8-03.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-CJ4Zty89.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-B5L2jPQ_.js → dialog-DL0Zftwv.js} +1 -1
  59. package/dist/dashboard/assets/dialog-DL0Zftwv.js.gz +0 -0
  60. package/dist/dashboard/assets/{drawer-DkjUouAj.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-B-LVLV6o.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-CAhdYl3B.js → entry-CGvdNU6r.js} +1 -1
  65. package/dist/dashboard/assets/entry-CGvdNU6r.js.gz +0 -0
  66. package/dist/dashboard/assets/entry-DScKYvZj.js +4 -0
  67. package/dist/dashboard/assets/entry-DScKYvZj.js.gz +0 -0
  68. package/dist/dashboard/assets/{formatters-CrsdLVsn.js → formatters-BS3DZlZj.js} +1 -1
  69. package/dist/dashboard/assets/formatters-BS3DZlZj.js.gz +0 -0
  70. package/dist/dashboard/assets/{index-BU64Lqvp.js → index-SQnvvTWX.js} +2 -2
  71. package/dist/dashboard/assets/index-SQnvvTWX.js.gz +0 -0
  72. package/dist/dashboard/assets/{inline-B2aDLyyg.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-BCVkdR9d.js → input-Bfx0EzRc.js} +1 -1
  75. package/dist/dashboard/assets/input-Bfx0EzRc.js.gz +0 -0
  76. package/dist/dashboard/assets/{layout-DNcAdMK-.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-DgrIH0hw.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-Cae01iu8.js → live-CbVTdX9V.js} +1 -1
  81. package/dist/dashboard/assets/live-CbVTdX9V.js.gz +0 -0
  82. package/dist/dashboard/assets/{network-CRib7H_2.js → network-JGvDveof.js} +1 -1
  83. package/dist/dashboard/assets/network-JGvDveof.js.gz +0 -0
  84. package/dist/dashboard/assets/{numbers-BKQE8Xpz.js → numbers-Dw6kiew-.js} +1 -1
  85. package/dist/dashboard/assets/numbers-Dw6kiew-.js.gz +0 -0
  86. package/dist/dashboard/assets/{pagination-ecPvG_8g.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/providers/zro.svg +3 -0
  91. package/dist/dashboard/assets/providers/zro.svg.gz +0 -0
  92. package/dist/dashboard/assets/quota-DiPD6UKH.js +1 -0
  93. package/dist/dashboard/assets/quota-DiPD6UKH.js.gz +0 -0
  94. package/dist/dashboard/assets/{routing-Ckm1UXuC.js → routing-8jZr-3WE.js} +1 -1
  95. package/dist/dashboard/assets/routing-8jZr-3WE.js.gz +0 -0
  96. package/dist/dashboard/assets/{select-ChoFWyKz.js → select-oO1FxMVb.js} +2 -2
  97. package/dist/dashboard/assets/select-oO1FxMVb.js.gz +0 -0
  98. package/dist/dashboard/assets/{settings-CKjFPlKW.js → settings-CLr3kdIx.js} +1 -1
  99. package/dist/dashboard/assets/settings-CLr3kdIx.js.gz +0 -0
  100. package/dist/dashboard/assets/{stack-DWCwU3fh.js → stack-Y3pDteZr.js} +1 -1
  101. package/dist/dashboard/assets/stack-Y3pDteZr.js.gz +0 -0
  102. package/dist/dashboard/assets/{state-Cg3VL5J_.js → state-CArX_Iak.js} +1 -1
  103. package/dist/dashboard/assets/state-CArX_Iak.js.gz +0 -0
  104. package/dist/dashboard/assets/{switch-Db70BMix.js → switch-DwlwmDO-.js} +1 -1
  105. package/dist/dashboard/assets/switch-DwlwmDO-.js.gz +0 -0
  106. package/dist/dashboard/assets/{useMutation-BqdE0LuM.js → useMutation-DJ9GYEgf.js} +1 -1
  107. package/dist/dashboard/assets/useMutation-DJ9GYEgf.js.gz +0 -0
  108. package/dist/dashboard/index.html +2 -2
  109. package/dist/dashboard/index.html.gz +0 -0
  110. package/dist/dashboard/precompressed-assets.json +54 -53
  111. package/migrations/full-postgres/0046_add_accounts_quota_plan_fetched_at.sql +7 -0
  112. package/migrations/full-postgres/0047_add_provider_device_flow_proxy.sql +17 -0
  113. package/migrations/lite-sqlite3/0046_add_accounts_quota_plan_fetched_at.sql +4 -0
  114. package/migrations/lite-sqlite3/0047_add_provider_device_flow_proxy.sql +19 -0
  115. package/package.json +1 -1
  116. package/src/boot/app.ts +11 -14
  117. package/src/boot/dependencies.ts +5 -7
  118. package/src/boot/flush.ts +20 -0
  119. package/src/boot/lifecycle.ts +7 -5
  120. package/src/config/providers.ts +6 -5
  121. package/src/config/routing.ts +26 -0
  122. package/src/config/spec-metadata.ts +72 -0
  123. package/src/config/spec.ts +28 -8
  124. package/src/console/api-keys/contracts.ts +12 -1
  125. package/src/console/api-keys/routes.ts +40 -28
  126. package/src/console/auth/account-reset.ts +7 -9
  127. package/src/console/backup/contracts.ts +4 -0
  128. package/src/console/backup/export-endpoint.ts +7 -5
  129. package/src/console/backup/nine-router.ts +71 -10
  130. package/src/console/backup/service.ts +8 -1
  131. package/src/console/cli-tools/service.ts +6 -12
  132. package/src/console/console-router-guards.ts +23 -16
  133. package/src/console/console-router.ts +1 -1
  134. package/src/console/domain-registration.ts +7 -4
  135. package/src/console/inferhub/client.ts +1 -1
  136. package/src/console/inferhub/normalize.ts +24 -28
  137. package/src/console/inferhub/types.ts +3 -0
  138. package/src/console/ip-bans/routes.ts +7 -13
  139. package/src/console/observability/live.ts +2 -2
  140. package/src/console/observability/store.ts +0 -53
  141. package/src/console/providers/catalog/contracts.ts +9 -3
  142. package/src/console/providers/catalog/model-operations.ts +2 -3
  143. package/src/console/providers/catalog/provider-operations.ts +1 -1
  144. package/src/console/providers/catalog/routes.ts +3 -2
  145. package/src/console/providers/catalog/store.ts +1914 -1902
  146. package/src/console/providers/detail/routes.ts +1 -0
  147. package/src/console/providers/detail/store.ts +4 -0
  148. package/src/console/providers/oauth/callback-listener.ts +30 -11
  149. package/src/console/providers/oauth/device-egress.ts +123 -0
  150. package/src/console/providers/oauth/routes.ts +123 -16
  151. package/src/console/quota/account-quota-global.ts +14 -24
  152. package/src/console/quota/account-quota-shared.ts +49 -0
  153. package/src/console/quota/account-quota-tenant.ts +11 -23
  154. package/src/console/quota/account-test-egress.ts +2 -2
  155. package/src/console/quota/daily-growth-job.ts +1 -1
  156. package/src/console/quota/quota-view.ts +19 -3
  157. package/src/console/routing/pools/contracts.ts +1 -1
  158. package/src/console/routing/pools/operations.ts +32 -47
  159. package/src/console/routing/pools/probe-verdict.ts +2 -2
  160. package/src/console/routing/pools/relay-deploy.ts +1 -1
  161. package/src/console/routing/pools/store.ts +4 -4
  162. package/src/console/shared/errors.ts +33 -6
  163. package/src/main.ts +2 -3
  164. package/src/network/client.ts +67 -0
  165. package/src/network/egress/bidi-session.ts +322 -0
  166. package/src/network/egress/dial.ts +201 -0
  167. package/src/network/egress/factory.ts +191 -0
  168. package/src/network/{outbound-fetch.ts → egress/fetch.ts} +195 -29
  169. package/src/network/egress/tls-profile.ts +123 -0
  170. package/src/network/egress/ws.ts +175 -0
  171. package/src/network/pool/{byte-accounting.ts → accounting.ts} +3 -2
  172. package/src/network/{types.ts → pool/agent-config.ts} +1 -1
  173. package/src/network/pool/agent.ts +2 -2
  174. package/src/network/{pool-cooldown-store.ts → pool/cooldown-store.ts} +95 -20
  175. package/src/network/{pool-health-machine.ts → pool/health.ts} +205 -6
  176. package/src/network/pool/resolver.ts +10 -849
  177. package/src/network/pool/selector.ts +184 -41
  178. package/src/network/ssrf.ts +7 -7
  179. package/src/network/{retry.ts → util/retry.ts} +1 -1
  180. package/src/network/{rate-limit-verdict.ts → verdict.ts} +1 -1
  181. package/src/observability/event-log.ts +7 -361
  182. package/src/observability/performance-metrics.ts +9 -0
  183. package/src/observability/runtime-metrics.ts +14 -9
  184. package/src/observability/telemetry-buffer.ts +16 -26
  185. package/src/observability/telemetry-reconciliation.ts +9 -37
  186. package/src/observability/telemetry-row.ts +125 -0
  187. package/src/persistence/db-mode.ts +4 -1
  188. package/src/persistence/dialect.ts +18 -0
  189. package/src/persistence/migrate.ts +6 -3
  190. package/src/persistence/schema.ts +17 -0
  191. package/src/persistence/ttl-cache.ts +21 -3
  192. package/src/protocol/codecs.ts +59 -2
  193. package/src/protocol/dsml.ts +34 -86
  194. package/src/protocol/markup-stream-parser.ts +195 -0
  195. package/src/protocol/reasoning-text.ts +19 -0
  196. package/src/protocol/request/chat.ts +10 -10
  197. package/src/protocol/request/gemini.ts +8 -2
  198. package/src/protocol/request/messages.ts +34 -22
  199. package/src/protocol/response/chat.ts +12 -20
  200. package/src/protocol/response/messages.ts +34 -7
  201. package/src/protocol/response/omp-tool-call-markup.ts +43 -86
  202. package/src/protocol/response/responses.ts +18 -2
  203. package/src/protocol/stop-reason.ts +23 -1
  204. package/src/protocol/think-split.ts +112 -0
  205. package/src/protocol/translation/dialect/qwen.ts +26 -58
  206. package/src/protocol/translation/quirks.ts +15 -0
  207. package/src/protocol/upstream/send-claude-messages.ts +2 -2
  208. package/src/providers/adapters/compatible-adapter.ts +76 -12
  209. package/src/providers/authentication/oauth-client.ts +40 -6
  210. package/src/providers/authentication/oauth-device-flow.ts +105 -11
  211. package/src/providers/authentication/oauth-flow-store.ts +82 -1
  212. package/src/providers/authentication/oauth-refresh-service.ts +766 -758
  213. package/src/providers/configured-provider.ts +148 -0
  214. package/src/providers/default-registry.ts +137 -36
  215. package/src/providers/discovery/discovery-types.ts +7 -0
  216. package/src/providers/discovery/probe-phases.ts +20 -6
  217. package/src/providers/discovery/probing-service.ts +22 -2
  218. package/src/providers/integrations/alibabacp/alibabacp-models.ts +1 -2
  219. package/src/providers/integrations/{dashscope-models.ts → alibabacp/dashscope-models.ts} +2 -2
  220. package/src/providers/integrations/antigravity/antigravity-protocol.ts +16 -1
  221. package/src/providers/integrations/buddy/account.ts +969 -0
  222. package/src/providers/integrations/buddy/capabilities.ts +12 -12
  223. package/src/providers/integrations/buddy/{buddy-catalog-shared.ts → catalog.ts} +198 -136
  224. package/src/providers/integrations/buddy/{buddy-chat-shared.ts → chat.ts} +176 -20
  225. package/src/providers/integrations/buddy/codebuddy/index.ts +429 -0
  226. package/src/providers/integrations/buddy/codebuddy/shared.ts +41 -26
  227. package/src/providers/integrations/buddy/headers.ts +311 -0
  228. package/src/providers/integrations/buddy/lifecycle.ts +1018 -0
  229. package/src/providers/integrations/buddy/{buddy-media.ts → media.ts} +1 -1
  230. package/src/providers/integrations/claude/claude-quota.ts +14 -17
  231. package/src/providers/integrations/cline/cline-oauth.ts +10 -5
  232. package/src/providers/integrations/cline/cline.ts +5 -6
  233. package/src/providers/integrations/clixad/clixad-oauth.ts +98 -0
  234. package/src/providers/integrations/clixad/clixad-quota.ts +132 -0
  235. package/src/providers/integrations/clixad/provider.ts +145 -0
  236. package/src/providers/integrations/codex/codex-oauth.ts +8 -5
  237. package/src/providers/integrations/commandcode/quota.ts +22 -28
  238. package/src/providers/integrations/cursor/cursor-auth.ts +1 -9
  239. package/src/providers/integrations/cursor/cursor-exec.ts +9 -2
  240. package/src/providers/integrations/cursor/cursor-hardening.ts +279 -0
  241. package/src/providers/integrations/cursor/cursor-http2.ts +605 -6
  242. package/src/providers/integrations/cursor/cursor-request.ts +26 -12
  243. package/src/providers/integrations/cursor/cursor-shared.ts +8 -5
  244. package/src/providers/integrations/cursor/cursor.ts +574 -110
  245. package/src/providers/integrations/cursor/protocol/messages.ts +22 -29
  246. package/src/providers/integrations/cursor/protocol/protobuf-schema.ts +37 -0
  247. package/src/providers/integrations/deepai/deepai-catalog.ts +149 -0
  248. package/src/providers/integrations/deepai/deepai-common.ts +145 -0
  249. package/src/providers/integrations/deepai/deepai-oauth.ts +129 -0
  250. package/src/providers/integrations/deepai/deepai-quota.ts +97 -0
  251. package/src/providers/integrations/deepai/provider.ts +42 -0
  252. package/src/providers/integrations/devin/devin-auth.ts +7 -0
  253. package/src/providers/integrations/devin/protocol/devin-proto.ts +1 -27
  254. package/src/providers/integrations/factory/factory-oauth.ts +33 -51
  255. package/src/providers/integrations/freebuff/assets/official-system-manager.txt +88 -0
  256. package/src/providers/integrations/freebuff/assets/official-system-worker.txt +57 -0
  257. package/src/providers/integrations/freebuff/assets/official-tools.json +1070 -0
  258. package/src/providers/integrations/freebuff/freebuff-catalog.ts +120 -0
  259. package/src/providers/integrations/freebuff/freebuff-constants.ts +1233 -0
  260. package/src/providers/integrations/freebuff/freebuff-discovery.ts +108 -0
  261. package/src/providers/integrations/freebuff/freebuff-oauth.ts +240 -0
  262. package/src/providers/integrations/freebuff/freebuff-quota.ts +260 -0
  263. package/src/providers/integrations/freebuff/freebuff-virtual.ts +89 -0
  264. package/src/providers/integrations/freebuff/freebuff.ts +682 -0
  265. package/src/providers/integrations/freepi/freepi-catalog.ts +5 -0
  266. package/src/providers/integrations/freepi/freepi-discovery.ts +2 -2
  267. package/src/providers/integrations/freepi/freepi-headers.ts +2 -2
  268. package/src/providers/integrations/freepi/freepi-protocol.ts +11 -2
  269. package/src/providers/integrations/freepi/freepi-realm.ts +0 -3
  270. package/src/providers/integrations/freepi/freepi-session.ts +158 -0
  271. package/src/providers/integrations/freepi/freepi.ts +80 -54
  272. package/src/providers/integrations/github/github-discovery.ts +30 -13
  273. package/src/providers/integrations/github/github-oauth.ts +217 -44
  274. package/src/providers/integrations/github/github-quota.ts +22 -5
  275. package/src/providers/integrations/github/github.ts +40 -32
  276. package/src/providers/integrations/grok/grok-oauth.ts +52 -49
  277. package/src/providers/integrations/grok/grok-ratelimit-store.ts +10 -17
  278. package/src/providers/integrations/grok/grok-turn-index.ts +11 -16
  279. package/src/providers/integrations/hermes/hermes-oauth.ts +3 -3
  280. package/src/providers/integrations/{hermes.ts → hermes/hermes.ts} +42 -13
  281. package/src/providers/integrations/hermes/index.ts +2 -0
  282. package/src/providers/integrations/kilo/kilo-oauth.ts +4 -4
  283. package/src/providers/integrations/kilo/kilo.ts +1 -1
  284. package/src/providers/integrations/kimi/kimi-oauth.ts +3 -3
  285. package/src/providers/integrations/kiro/kiro-catalog.ts +17 -1
  286. package/src/providers/integrations/kiro/kiro-discovery.ts +10 -12
  287. package/src/providers/integrations/kiro/kiro-oauth.ts +2 -2
  288. package/src/providers/integrations/kiro/kiro-quota.ts +8 -4
  289. package/src/providers/integrations/kiro/kiro.ts +15 -19
  290. package/src/providers/integrations/muse/muse-oauth.ts +5 -5
  291. package/src/providers/integrations/ollamacloud/index.ts +2 -0
  292. package/src/providers/integrations/{ollamacloud-models.ts → ollamacloud/models.ts} +2 -2
  293. package/src/providers/integrations/{ollamacloud-quota.ts → ollamacloud/quota.ts} +3 -3
  294. package/src/providers/integrations/opencode/fingerprint.ts +1 -1
  295. package/src/providers/integrations/opencode/opencode-go-quota.ts +2 -8
  296. package/src/providers/integrations/openrouter/provider.ts +1 -1
  297. package/src/providers/integrations/perch/perch-credential.ts +25 -0
  298. package/src/providers/integrations/perch/perch-protocol.ts +0 -6
  299. package/src/providers/integrations/perch/perch-quota.ts +2 -16
  300. package/src/providers/integrations/perch/perch.ts +6 -16
  301. package/src/providers/integrations/qoder/claim-qoder-credits.ts +19 -60
  302. package/src/providers/integrations/qoder/qoder-adapter.ts +20 -125
  303. package/src/providers/integrations/qoder/qoder-auth.ts +109 -30
  304. package/src/providers/integrations/qoder/qoder-catalog.ts +21 -6
  305. package/src/providers/integrations/qoder/qoder-chat.ts +32 -29
  306. package/src/providers/integrations/qoder/qoder-checkin.ts +5 -5
  307. package/src/providers/integrations/qoder/qoder-cosy.ts +37 -9
  308. package/src/providers/integrations/qoder/qoder-oauth.ts +9 -2
  309. package/src/providers/integrations/qoder/qoder-realm.ts +13 -36
  310. package/src/providers/integrations/qoder/qoder-sse.ts +447 -68
  311. package/src/providers/integrations/qoder/qoder-types.ts +9 -0
  312. package/src/providers/integrations/qoder/qoder.ts +3 -26
  313. package/src/providers/integrations/sensenova/provider.ts +144 -0
  314. package/src/providers/integrations/trae/trae-protocol.ts +9 -5
  315. package/src/providers/integrations/trae/trae-quota.ts +21 -29
  316. package/src/providers/integrations/trae/trae-realm.ts +38 -0
  317. package/src/providers/integrations/trae/trae.ts +7 -24
  318. package/src/providers/integrations/vercel/provider.ts +117 -0
  319. package/src/providers/integrations/vercel/vercel-quota.ts +96 -0
  320. package/src/providers/integrations/xai/xai-oauth.ts +5 -5
  321. package/src/providers/integrations/zcode-start-plan/gifts.ts +1 -1
  322. package/src/providers/integrations/zcode-start-plan/oauth.ts +3 -3
  323. package/src/providers/integrations/zcode-start-plan/provider.ts +226 -23
  324. package/src/providers/integrations/zcode-start-plan/quota.ts +19 -2
  325. package/src/providers/integrations/zro/zro.ts +205 -0
  326. package/src/providers/operations/account-error-classification.ts +199 -307
  327. package/src/providers/operations/account-health-service.ts +82 -1
  328. package/src/providers/operations/account-refresh-status.ts +36 -37
  329. package/src/providers/operations/account-tier.ts +15 -16
  330. package/src/providers/operations/client-version-resolver.ts +1 -1
  331. package/src/providers/operations/client-versions.ts +100 -81
  332. package/src/providers/operations/cooldown-rules.ts +11 -16
  333. package/src/providers/operations/oauth-refresh-lead.ts +46 -22
  334. package/src/providers/operations/{model-catalog-cache.ts → provider-caches.ts} +68 -16
  335. package/src/providers/operations/provider-catalog-service.ts +1 -1
  336. package/src/providers/operations/provider-credential-service.ts +118 -51
  337. package/src/providers/operations/provider-rules/github.ts +48 -0
  338. package/src/providers/operations/provider-rules/grok.ts +33 -0
  339. package/src/providers/operations/provider-rules/inception.ts +49 -0
  340. package/src/providers/operations/provider-rules/index.ts +32 -0
  341. package/src/providers/operations/provider-rules/perch.ts +37 -0
  342. package/src/providers/operations/provider-rules/qoder.ts +70 -0
  343. package/src/providers/operations/provider-rules/trae.ts +81 -0
  344. package/src/providers/operations/session-resolution.ts +17 -1
  345. package/src/providers/provider-contracts.ts +6 -4
  346. package/src/providers/{provider-credential-resolver.ts → provider-credentials.ts} +40 -3
  347. package/src/providers/provider-metadata.ts +70 -10
  348. package/src/providers/provider-probe.ts +15 -1
  349. package/src/providers/provider-registry.ts +42 -33
  350. package/src/providers/quota/quota-contracts.ts +2 -0
  351. package/src/providers/quota/quota-limit-windows.ts +16 -5
  352. package/src/providers/quota/quota-support.ts +1 -1
  353. package/src/providers/quota/quota-window-parser.ts +90 -18
  354. package/src/providers/quota/refresh.ts +100 -21
  355. package/src/security/api-key-auth-cache.ts +1 -1
  356. package/src/security/api-key-auth.ts +6 -2
  357. package/src/security/ip-boundary.ts +1 -1
  358. package/src/transport/dispatch/attempt-finalize.ts +15 -14
  359. package/src/transport/dispatch/attempt-loop.ts +73 -19
  360. package/src/transport/dispatch/generation.ts +2 -8
  361. package/src/transport/dispatch/handler-deps.ts +1 -1
  362. package/src/transport/dispatch/leases.ts +1 -1
  363. package/src/transport/dispatch/native-shared.ts +25 -0
  364. package/src/transport/dispatch/proxy-request.ts +8 -46
  365. package/src/transport/dispatch/responses-compact.ts +2 -8
  366. package/src/transport/dispatch/retry-policy.ts +1 -1
  367. package/src/transport/dispatch/streaming/finalize.ts +10 -7
  368. package/src/transport/dispatch/streaming/stream.ts +9 -37
  369. package/src/transport/dispatch/systemone.ts +2 -8
  370. package/src/transport/dispatch/token-count.ts +2 -6
  371. package/src/transport/dispatch/upstream.ts +63 -0
  372. package/src/transport/drain-endpoint.ts +31 -4
  373. package/src/transport/failure-policy.ts +16 -30
  374. package/src/transport/guards/body-policy.ts +7 -1
  375. package/src/transport/guards/error-lifecycle.ts +13 -1
  376. package/src/transport/guards/request-context.ts +22 -14
  377. package/src/transport/request/preparer.ts +81 -71
  378. package/src/transport/routing/bounded-lru.ts +14 -0
  379. package/src/transport/routing/drain-reserve.ts +129 -0
  380. package/src/transport/routing/plan-gate.ts +7 -1
  381. package/src/transport/routing/round-robin.ts +60 -0
  382. package/src/transport/routing/route-catalog.ts +123 -111
  383. package/src/transport/routing/route-model.ts +51 -9
  384. package/src/transport/routing/router.ts +393 -32
  385. package/src/transport/shutdown-notice.ts +1 -1
  386. package/src/transport/surface/adapters.ts +11 -2
  387. package/src/transport/surface/chat/adapter.ts +2 -2
  388. package/src/transport/surface/chat/encode.ts +47 -4
  389. package/src/transport/surface/chat/parse.ts +2 -12
  390. package/src/transport/surface/completion.ts +44 -33
  391. package/src/transport/surface/content-parts.ts +21 -0
  392. package/src/transport/surface/messages/adapter.ts +7 -1
  393. package/src/transport/surface/messages/encode.ts +11 -3
  394. package/src/transport/surface/messages/parse.ts +2 -1
  395. package/src/transport/surface/messages/stream.ts +33 -17
  396. package/src/transport/surface/responses/encode.ts +22 -5
  397. package/src/transport/surface/responses/parse.ts +3 -11
  398. package/src/workers/buddy-growth-tasks.ts +8 -8
  399. package/src/workers/daily-checkin.ts +4 -4
  400. package/src/workers/quota-refresh-sweep.ts +1 -1
  401. package/src/workers/zcode-gift-claim.ts +1 -1
  402. package/dist/dashboard/assets/BannedPage-BxoG1MrL.js.gz +0 -0
  403. package/dist/dashboard/assets/CliToolDetailPage-ixwgKk53.js.gz +0 -0
  404. package/dist/dashboard/assets/CliToolsPage-BmttNV3U.js.gz +0 -0
  405. package/dist/dashboard/assets/CombosPage-BE3qwwfC.js.gz +0 -0
  406. package/dist/dashboard/assets/ConsoleLogPage-CRZdmMIX.js.gz +0 -0
  407. package/dist/dashboard/assets/CustomizationPage-Dv_FJ6Er.js.gz +0 -0
  408. package/dist/dashboard/assets/HealthEventsModal-CoVrw2sM.js +0 -1
  409. package/dist/dashboard/assets/HealthEventsModal-CoVrw2sM.js.gz +0 -0
  410. package/dist/dashboard/assets/InjectSystemPromptPanel-BOoO8rpz.js.gz +0 -0
  411. package/dist/dashboard/assets/LoginPage-ChJuNNVN.js.gz +0 -0
  412. package/dist/dashboard/assets/ModelPicker-BZAJtPg1.js.gz +0 -0
  413. package/dist/dashboard/assets/OverviewPage-BE40s3u5.js.gz +0 -0
  414. package/dist/dashboard/assets/ProviderDetailPage-sfLoQtPV.js +0 -8
  415. package/dist/dashboard/assets/ProviderDetailPage-sfLoQtPV.js.gz +0 -0
  416. package/dist/dashboard/assets/ProviderIcon-CQ6bPUf_.js.gz +0 -0
  417. package/dist/dashboard/assets/ProvidersPage-DcepmPWo.js +0 -1
  418. package/dist/dashboard/assets/ProvidersPage-DcepmPWo.js.gz +0 -0
  419. package/dist/dashboard/assets/ProxyPage-BJ87Pp0U.js +0 -9
  420. package/dist/dashboard/assets/ProxyPage-BJ87Pp0U.js.gz +0 -0
  421. package/dist/dashboard/assets/QuotaPage-gkSyRAKa.js +0 -1
  422. package/dist/dashboard/assets/QuotaPage-gkSyRAKa.js.gz +0 -0
  423. package/dist/dashboard/assets/SettingsPage-C5HPWqEv.js.gz +0 -0
  424. package/dist/dashboard/assets/SetupPage-CFNcATJJ.js.gz +0 -0
  425. package/dist/dashboard/assets/StudioPage-YoD4tY5l.js.gz +0 -0
  426. package/dist/dashboard/assets/UsagePage-DxjaAhb_.js.gz +0 -0
  427. package/dist/dashboard/assets/api-keys-BTlkq_Uk.js.gz +0 -0
  428. package/dist/dashboard/assets/app-BX2nnKqs.css +0 -2
  429. package/dist/dashboard/assets/app-BX2nnKqs.css.gz +0 -0
  430. package/dist/dashboard/assets/assert-CndthXFQ.js +0 -1
  431. package/dist/dashboard/assets/assert-CndthXFQ.js.gz +0 -0
  432. package/dist/dashboard/assets/badge-DdbT_7Am.js.gz +0 -0
  433. package/dist/dashboard/assets/banner-CsGN-nbm.js.gz +0 -0
  434. package/dist/dashboard/assets/card-mgZ_TLLZ.js.gz +0 -0
  435. package/dist/dashboard/assets/cli-tools-A1-v8-03.js.gz +0 -0
  436. package/dist/dashboard/assets/clipboard-button-CJ4Zty89.js.gz +0 -0
  437. package/dist/dashboard/assets/dialog-B5L2jPQ_.js.gz +0 -0
  438. package/dist/dashboard/assets/drawer-DkjUouAj.js.gz +0 -0
  439. package/dist/dashboard/assets/entry-B-LVLV6o.js.gz +0 -0
  440. package/dist/dashboard/assets/entry-CAhdYl3B.js.gz +0 -0
  441. package/dist/dashboard/assets/entry-D2xD0Tmd.js +0 -4
  442. package/dist/dashboard/assets/entry-D2xD0Tmd.js.gz +0 -0
  443. package/dist/dashboard/assets/formatters-CrsdLVsn.js.gz +0 -0
  444. package/dist/dashboard/assets/index-BU64Lqvp.js.gz +0 -0
  445. package/dist/dashboard/assets/inline-B2aDLyyg.js.gz +0 -0
  446. package/dist/dashboard/assets/input-BCVkdR9d.js.gz +0 -0
  447. package/dist/dashboard/assets/layout-DNcAdMK-.js.gz +0 -0
  448. package/dist/dashboard/assets/list-row-DgrIH0hw.js.gz +0 -0
  449. package/dist/dashboard/assets/live-Cae01iu8.js.gz +0 -0
  450. package/dist/dashboard/assets/network-CRib7H_2.js.gz +0 -0
  451. package/dist/dashboard/assets/numbers-BKQE8Xpz.js.gz +0 -0
  452. package/dist/dashboard/assets/pagination-ecPvG_8g.js.gz +0 -0
  453. package/dist/dashboard/assets/quota-DJ1QlAOq.js +0 -1
  454. package/dist/dashboard/assets/quota-DJ1QlAOq.js.gz +0 -0
  455. package/dist/dashboard/assets/routing-Ckm1UXuC.js.gz +0 -0
  456. package/dist/dashboard/assets/select-ChoFWyKz.js.gz +0 -0
  457. package/dist/dashboard/assets/settings-CKjFPlKW.js.gz +0 -0
  458. package/dist/dashboard/assets/stack-DWCwU3fh.js.gz +0 -0
  459. package/dist/dashboard/assets/state-Cg3VL5J_.js.gz +0 -0
  460. package/dist/dashboard/assets/switch-Db70BMix.js.gz +0 -0
  461. package/dist/dashboard/assets/useMutation-BqdE0LuM.js.gz +0 -0
  462. package/src/network/pool-health.ts +0 -210
  463. package/src/providers/bundled-discovery-providers.ts +0 -98
  464. package/src/providers/configured-openai-providers.ts +0 -120
  465. package/src/providers/integrations/buddy/buddy-checkin.ts +0 -521
  466. package/src/providers/integrations/buddy/buddy-cli-headers.ts +0 -76
  467. package/src/providers/integrations/buddy/buddy-conversation-headers.ts +0 -130
  468. package/src/providers/integrations/buddy/buddy-discovery-shared.ts +0 -234
  469. package/src/providers/integrations/buddy/buddy-growth-shared.ts +0 -496
  470. package/src/providers/integrations/buddy/buddy-identity.ts +0 -300
  471. package/src/providers/integrations/buddy/buddy-oauth-shared.ts +0 -341
  472. package/src/providers/integrations/buddy/buddy-quota-shared.ts +0 -417
  473. package/src/providers/integrations/buddy/buddy-server-tools.ts +0 -240
  474. package/src/providers/integrations/buddy/buddy-usage-shared.ts +0 -221
  475. package/src/providers/integrations/buddy/buddy-usage-store.ts +0 -181
  476. package/src/providers/integrations/buddy/codebuddy/cn-catalog.ts +0 -37
  477. package/src/providers/integrations/buddy/codebuddy/cn-provider.ts +0 -185
  478. package/src/providers/integrations/buddy/codebuddy/oauth.ts +0 -47
  479. package/src/providers/integrations/buddy/codebuddy/provider.ts +0 -175
  480. package/src/providers/integrations/buddy/codebuddy/quota.ts +0 -154
  481. package/src/providers/integrations/qoder/chat-proto.ts +0 -328
  482. package/src/providers/integrations/qoder/qoder-grpc.ts +0 -871
  483. package/src/providers/integrations/qoder/qoder-ide.ts +0 -588
  484. package/src/providers/operations/model-discovery-cache.ts +0 -34
  485. package/src/providers/operations/probe-fetch.ts +0 -15
  486. package/src/providers/operations/provider-version-cache.ts +0 -30
  487. package/src/providers/provider-capabilities.ts +0 -33
  488. package/src/providers/provider-credential-envelope.ts +0 -43
  489. /package/dist/dashboard/assets/{app-B7kIWFm5.js → app-C0gTXEoN.js} +0 -0
  490. /package/dist/dashboard/assets/{app-B7kIWFm5.js.gz → app-C0gTXEoN.js.gz} +0 -0
  491. /package/src/network/{http2-bidi.ts → bidi.ts} +0 -0
  492. /package/src/network/pool/{pool-scripts.ts → scripts.ts} +0 -0
  493. /package/src/network/pool/{pool-state.ts → state.ts} +0 -0
  494. /package/src/network/{backoff.ts → util/backoff.ts} +0 -0
  495. /package/src/network/{deduplication.ts → util/dedupe.ts} +0 -0
  496. /package/src/network/{ip-utils.ts → util/ip.ts} +0 -0
@@ -196,6 +196,7 @@ export async function recordAccountFailure(
196
196
  // 1m limit must keep its short cooldown, not park the account for 24h).
197
197
  const autoDisableTarget =
198
198
  AUTO_DISABLE_TARGET_CATEGORIES.has(effective.category) &&
199
+ effective.modelScoped !== true &&
199
200
  effective.status === "cooldown" &&
200
201
  effective.mutatesAccount &&
201
202
  options?.origin === "upstream";
@@ -230,7 +231,10 @@ export async function recordAccountFailure(
230
231
  // D02: when the auto-disable intercept fired, the target category must not
231
232
  // sink into a model-scoped cooldown — the outcome is an account-wide
232
233
  // disable.
233
- const shouldModelCooldown = !autoDisableActive && modelId !== undefined && isThrottle;
234
+ const shouldModelCooldown =
235
+ !autoDisableActive &&
236
+ modelId !== undefined &&
237
+ (isThrottle || effective.modelScoped === true);
234
238
  // The account-wide streak, escalated for a provider-stated window. Read once
235
239
  // so the model-scoped and account-wide paths below cannot disagree about it:
236
240
  // this is the count *before* this failure, which the UPDATE below increments.
@@ -694,6 +698,83 @@ async function writeHealthEvent(
694
698
  await client.insert(healthEvents).values({ id: randomUUID(), ...row });
695
699
  }
696
700
 
701
+ /**
702
+ * The one internal seam that applies a health verdict to an account row.
703
+ *
704
+ * Every producer that persists account-state semantics — dispatch failure
705
+ * classification, OAuth refresh definitive failure, quota refresh parking —
706
+ * converges here instead of writing `status`/`cooldownUntil`/error fields
707
+ * independently. Producers may still classify differently; they may not
708
+ * persist account-state semantics separately.
709
+ *
710
+ * This writes the account-wide health state plus a health audit row, inside the
711
+ * caller's transaction (`client`). It returns `true` when the account row was
712
+ * actually updated (i.e. the account exists and is not operator-disabled with
713
+ * `preserveOperatorDisable`), so the caller knows whether routing must be
714
+ * invalidated (routesVisible) or the lease released.
715
+ */
716
+ export async function applyAccountVerdict(
717
+ client: CartethyiaDatabase,
718
+ verdict: {
719
+ readonly accountId: string;
720
+ readonly status: "cooldown" | "disabled";
721
+ readonly reason: string;
722
+ readonly category: string | null;
723
+ readonly cooldownUntil?: Date | null;
724
+ /** When true, an operator-disable (status `disabled`, no deadline) is left untouched. */
725
+ readonly preserveOperatorDisable?: boolean;
726
+ /** Health event transition status (defaults to the verdict status). */
727
+ readonly fromStatus?: "active" | "cooldown" | "disabled" | null;
728
+ readonly modelId?: string | null;
729
+ readonly createdAt?: Date;
730
+ },
731
+ ): Promise<boolean> {
732
+ return withTransaction(
733
+ client,
734
+ async (tx) => {
735
+ const rows = await tx
736
+ .select({
737
+ status: providerAccounts.status,
738
+ cooldownUntil: providerAccounts.cooldownUntil,
739
+ })
740
+ .from(providerAccounts)
741
+ .where(eq(providerAccounts.id, verdict.accountId))
742
+ .limit(1);
743
+ const row = rows[0];
744
+ if (!row) return false;
745
+ if (verdict.preserveOperatorDisable === true) {
746
+ const isOperatorDisable = row.status === "disabled" && row.cooldownUntil == null;
747
+ if (isOperatorDisable) return false;
748
+ }
749
+ const now = verdict.createdAt ?? new Date();
750
+ await tx
751
+ .update(providerAccounts)
752
+ .set({
753
+ status: verdict.status,
754
+ lastError: (verdict.reason ?? "Account failed").slice(0, 500),
755
+ lastErrorCategory: verdict.category ?? "check_failed",
756
+ lastErrorAt: now,
757
+ ...(verdict.cooldownUntil === undefined
758
+ ? {}
759
+ : { cooldownUntil: verdict.cooldownUntil }),
760
+ })
761
+ .where(eq(providerAccounts.id, verdict.accountId));
762
+ await writeHealthEvent(tx, {
763
+ entityKind: "account",
764
+ accountId: verdict.accountId,
765
+ fromStatus: verdict.fromStatus ?? (row.status as "active" | "cooldown" | "disabled" | null),
766
+ toStatus: verdict.status,
767
+ reason: verdict.reason,
768
+ errorCategory: verdict.category,
769
+ modelId: verdict.modelId ?? null,
770
+ createdAt: now,
771
+ });
772
+ return true;
773
+ },
774
+ false,
775
+ );
776
+ }
777
+
697
778
  export async function reportAttemptOutcome(
698
779
  db: CartethyiaDatabase,
699
780
  report: AttemptHealthReport,
@@ -9,10 +9,10 @@
9
9
  * report `"active"` just because a refresh succeeded.
10
10
  */
11
11
  import { randomUUID } from "node:crypto";
12
- import { eq } from "drizzle-orm";
13
12
  import type { CartethyiaDatabase } from "../../persistence/db-handle";
14
- import { healthEvents, providerAccounts, providerOauthStates } from "../../persistence/schema";
13
+ import { healthEvents } from "../../persistence/schema";
15
14
  import { pushStructuredConsoleLog } from "../../observability/log-ring";
15
+ import { loadAccountWithOauthState } from "./provider-credential-service";
16
16
  import type { OAuthRefreshService, OAuthTokenRefresher } from "../authentication/oauth-refresh-service";
17
17
 
18
18
  export type RefreshAccountStatusCode =
@@ -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
  }
@@ -62,47 +64,34 @@ async function loadAccount(
62
64
  db: CartethyiaDatabase,
63
65
  accountId: string,
64
66
  ): Promise<LoadedAccount | undefined> {
65
- const selectBuilder = db
66
- .select({
67
- id: providerAccounts.id,
68
- providerId: providerAccounts.providerId,
69
- credentialKind: providerAccounts.credentialKind,
70
- status: providerAccounts.status,
71
- staticToken: providerAccounts.staticToken,
72
- refreshCiphertext: providerOauthStates.refreshCiphertext,
73
- expiresAt: providerOauthStates.expiresAt,
74
- })
75
- .from(providerAccounts);
76
- const query =
77
- "leftJoin" in selectBuilder
78
- ? selectBuilder.leftJoin(
79
- providerOauthStates,
80
- eq(providerOauthStates.providerAccountId, providerAccounts.id),
81
- )
82
- : selectBuilder;
83
- const rows = await query.where(eq(providerAccounts.id, accountId)).limit(1);
84
- return rows[0];
67
+ const loaded = await loadAccountWithOauthState(db, accountId, {
68
+ includeStatus: true,
69
+ includeLastErrorCategory: true,
70
+ includeExpiry: true,
71
+ });
72
+ if (!loaded || loaded.status === undefined || loaded.expiresAt === undefined) return undefined;
73
+ return {
74
+ id: loaded.id,
75
+ providerId: loaded.providerId,
76
+ credentialKind: loaded.credentialKind,
77
+ status: loaded.status,
78
+ staticToken: loaded.staticToken,
79
+ lastErrorCategory: loaded.lastErrorCategory ?? null,
80
+ refreshCiphertext: loaded.refreshCiphertext,
81
+ expiresAt: loaded.expiresAt,
82
+ };
85
83
  }
86
84
 
87
85
  async function readStatusAfter(
88
86
  db: CartethyiaDatabase,
89
87
  accountId: string,
90
88
  ): Promise<{ status: string; expiresAt: Date | null } | undefined> {
91
- const selectBuilder = db
92
- .select({
93
- status: providerAccounts.status,
94
- expiresAt: providerOauthStates.expiresAt,
95
- })
96
- .from(providerAccounts);
97
- const query =
98
- "leftJoin" in selectBuilder
99
- ? selectBuilder.leftJoin(
100
- providerOauthStates,
101
- eq(providerOauthStates.providerAccountId, providerAccounts.id),
102
- )
103
- : selectBuilder;
104
- const rows = await query.where(eq(providerAccounts.id, accountId)).limit(1);
105
- return rows[0];
89
+ const loaded = await loadAccountWithOauthState(db, accountId, {
90
+ includeStatus: true,
91
+ includeExpiry: true,
92
+ });
93
+ if (!loaded || loaded.status === undefined || loaded.expiresAt === undefined) return undefined;
94
+ return { status: loaded.status, expiresAt: loaded.expiresAt };
106
95
  }
107
96
 
108
97
  /** Non-mutating health event: records the attempt, never changes status. */
@@ -152,6 +141,16 @@ export async function refreshAccountStatus(
152
141
  }
153
142
  const statusBefore = account.status;
154
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
+ }
155
154
  if (account.credentialKind !== "oauth") {
156
155
  return {
157
156
  ok: false,
@@ -8,24 +8,22 @@
8
8
  *
9
9
  * The mapping is deliberately conservative, following the same posture as
10
10
  * `plan-gate.ts`: a tier is only claimed when the string proves it. "free"
11
- * requires a free marker in the string, "pro" requires a team/enterprise/org
12
- * signal, and "paid" is only assigned for providers whose plan strings are
13
- * evidenced (Trae, the buddy family, Cursor). Everything else — an unknown
14
- * provider, an unrecognized plan, a `null` plan — resolves to `null`, which
15
- * keeps the legacy behavior intact (unknown tier ⇒ no rule applies ⇒ the old
16
- * constants drive the cooldown).
11
+ * requires the shared free marker set (exported there), "pro" requires a
12
+ * team/enterprise/org signal, and "paid" is only assigned for providers whose
13
+ * plan strings are evidenced (Trae, the buddy family, Cursor). Everything else
14
+ * — an unknown provider, an unrecognized plan, a `null` plan — resolves to
15
+ * `null`, which keeps the legacy behavior intact (unknown tier ⇒ no rule
16
+ * applies ⇒ the old constants drive the cooldown).
17
17
  */
18
18
 
19
+ import { FREE_PLAN_MARKERS } from "../../transport/routing/plan-gate";
20
+ import { BUDDY_PROVIDER_IDS } from "../provider-metadata";
21
+ import { QODER_CHECKIN_PROVIDER_IDS } from "../integrations/qoder/qoder-checkin";
22
+ import { TRAE_PROVIDER_IDS } from "../integrations/trae/trae-realm";
23
+
19
24
  /** The three-tier taxonomy for cooldown rules. `null` = unknown. */
20
25
  export type AccountTier = "free" | "paid" | "pro" | null;
21
26
 
22
- const TRAE_PROVIDER_IDS: ReadonlySet<string> = new Set(["trae", "traecn"]);
23
- const BUDDY_PROVIDER_IDS: ReadonlySet<string> = new Set([
24
- "cb",
25
- "cbcn",
26
- ]);
27
- const QODER_PROVIDER_IDS: ReadonlySet<string> = new Set(["qoder", "qodercn"]);
28
-
29
27
  /** Team/enterprise/org signals that prove a "pro" tier. */
30
28
  const PRO_PLAN_MARKERS: readonly string[] = ["team", "enterprise", "org", "pro"];
31
29
 
@@ -41,10 +39,11 @@ export function mapPlanToTier(providerId: string, plan: string | null): AccountT
41
39
  const lower = (plan ?? "").trim().toLowerCase();
42
40
  if (lower.length === 0) return null;
43
41
  // Free is only claimed when the string proves it — the same marker set the
44
- // routing plan-gate uses, so the two cannot disagree about an account.
45
- if (lower.includes("free")) return "free";
42
+ // routing plan-gate uses (imported, not copied), so the two cannot disagree
43
+ // about an account.
44
+ if (FREE_PLAN_MARKERS.some((marker) => lower.includes(marker))) return "free";
46
45
 
47
- if (QODER_PROVIDER_IDS.has(providerId.toLowerCase())) {
46
+ if ((QODER_CHECKIN_PROVIDER_IDS as readonly string[]).includes(providerId.toLowerCase())) {
48
47
  // Qoder's `userType` is `personal_standard` by default; team/enterprise/
49
48
  // org suffixes prove a higher tier. `personal_standard` itself is not
50
49
  // verified against the taxonomy, so it stays unknown rather than guessed.
@@ -11,7 +11,7 @@
11
11
  * Discovery is owned by the background client-version monitor. Request paths
12
12
  * only call the synchronous `get`/`snapshot` accessors and never fetch.
13
13
  */
14
- import { getCachedVersion, resetVersionCacheForTesting } from "./provider-version-cache";
14
+ import { getCachedVersion, resetVersionCacheForTesting } from "./provider-caches";
15
15
  import type { OutboundFetch } from "../provider-contracts";
16
16
  import { composeAbortSignal } from "../authentication/oauth-flow-store";
17
17
 
@@ -16,28 +16,19 @@ import {
16
16
  buildGrokShellUserAgent,
17
17
  } from "./cli-platform";
18
18
 
19
- async function qoderVersion(response: Response): Promise<string | null> {
20
- try {
21
- const parsed = (await response.json()) as { version?: unknown };
22
- return isSemverish(parsed.version) ? parsed.version.trim() : null;
23
- } catch {
24
- return null;
25
- }
26
- }
27
-
28
19
  /**
29
20
  * Newest Trae IDE version from the official changelog.
30
21
  *
31
- * Trae publishes no npm package and no release API, so the changelog is the
22
+ * Trae publishes no npm package and no release API, so the changelog was the
32
23
  * only machine-readable source of the current client version. It matters
33
24
  * upstream: intl `llm_raw_chat` answers `4015` unless the request carries the
34
25
  * *current* client version, so a stale pin is an outage, not a cosmetic drift.
35
26
  *
36
- * The page embeds its content as JSON, and its newest entry is the first dated
37
- * heading — not the highest semver, because this vendor's hotfix line numbers
38
- * past its feature line (3.5.104 shipped after 3.5.97). Taking `max(semver)`
39
- * would report an old build as current, so this walks the document in order and
40
- * returns the first version following a dated release heading.
27
+ * The page is now an SPA (2026-11-11): its HTML no longer embeds the release
28
+ * data, so this parser returns null and the pinned fallback carries the wire
29
+ * identity until a machine-readable feed exists again (see
30
+ * `VERSION_SOURCES.trae`). The parser is kept for when the page re-exposes
31
+ * its entries.
41
32
  */
42
33
  async function traeVersion(response: Response): Promise<string | null> {
43
34
  try {
@@ -67,11 +58,6 @@ async function traeVersion(response: Response): Promise<string | null> {
67
58
  }
68
59
  }
69
60
 
70
- async function clineClientVersion(response: Response): Promise<string | null> {
71
- const data = (await response.json()) as { version?: unknown };
72
- return isSemverish(data.version) ? data.version.trim() : null;
73
- }
74
-
75
61
  /**
76
62
  * Newest Cursor build from the official download API, as the IDE's own header
77
63
  * spells it.
@@ -95,29 +81,6 @@ async function cursorVersion(response: Response): Promise<string | null> {
95
81
  }
96
82
  }
97
83
 
98
- async function clineSdkVersion(response: Response): Promise<string | null> {
99
- const data = (await response.json()) as {
100
- version?: unknown;
101
- "dist-tags"?: { latest?: unknown };
102
- };
103
- const candidate = data.version ?? data["dist-tags"]?.latest;
104
- return isSemverish(candidate) ? candidate.trim() : null;
105
- }
106
-
107
- async function kimiVersion(response: Response): Promise<string | null> {
108
- const data = (await response.json()) as { info?: { version?: unknown } };
109
- return isSemverish(data.info?.version) ? data.info.version.trim() : null;
110
- }
111
-
112
- async function kiroCaskVersion(response: Response): Promise<string | null> {
113
- try {
114
- const data = (await response.json()) as { version?: unknown };
115
- return isSemverish(data.version) ? data.version.trim() : null;
116
- } catch {
117
- return null;
118
- }
119
- }
120
-
121
84
  async function codeBuddyCnVersion(response: Response): Promise<string | null> {
122
85
  try {
123
86
  const data = (await response.json()) as { version?: unknown };
@@ -216,11 +179,24 @@ const KIRO_SYSTEM_VERSION = "win32#10.0.22631";
216
179
  /** Node version the observed Kiro client reports (Electron's bundled Node). */
217
180
  const KIRO_NODE_VERSION = "24.18.0";
218
181
 
182
+ /** Clixad gateway's own CLI version: `cli_latest` on `/v1/models`. */
183
+ async function clixadVersion(response: Response): Promise<string | null> {
184
+ const body = await response.text().catch(() => "");
185
+ if (body.length === 0) return null;
186
+ try {
187
+ const parsed = JSON.parse(body) as { cli_latest?: unknown };
188
+ const candidate = parsed.cli_latest;
189
+ return typeof candidate === "string" && isSemverish(candidate) ? candidate.trim() : null;
190
+ } catch {
191
+ return null;
192
+ }
193
+ }
194
+
219
195
  /** Ordered upstream sources and pinned fallbacks for every client version. */
220
196
  export const VERSION_SOURCES = {
221
197
  antigravity: {
222
198
  key: "antigravity",
223
- fallback: "2.29.0",
199
+ fallback: "2.31.0",
224
200
  sources: [
225
201
  {
226
202
  url: "https://antigravity-hub-auto-updater-974169037036.us-central1.run.app/manifest/latest-arm64-mac.yml",
@@ -236,12 +212,7 @@ export const VERSION_SOURCES = {
236
212
  // `@qoder-ai/qodercli/latest`) may report a newer CLI, but the CLI and
237
213
  // IDE are different products — the chat surface identity is the IDE's.
238
214
  fallback: "1.1.80",
239
- sources: [
240
- {
241
- url: "https://registry.npmjs.org/@qoder-ai/qodercli/latest",
242
- extract: qoderVersion,
243
- },
244
- ],
215
+ sources: [{ url: "https://registry.npmjs.org/@qoder-ai/qodercli/latest" }],
245
216
  },
246
217
  opencode: {
247
218
  key: "opencode",
@@ -249,7 +220,7 @@ export const VERSION_SOURCES = {
249
220
  // under `@opencode/cli`, and this feed is what its own installer reads.
250
221
  // Fallback = the current latest release; raise it whenever the package
251
222
  // moves so a cold process does not present a stale client build.
252
- fallback: "2.2.6",
223
+ fallback: "2.2.7",
253
224
  minVersion: "2.0.0",
254
225
  sources: [{ url: "https://opencode.ai/update/api/latest/cli/npm" }],
255
226
  },
@@ -274,49 +245,45 @@ export const VERSION_SOURCES = {
274
245
  fallback: "4.1.26",
275
246
  minVersion: "4.1.23",
276
247
  sources: [
277
- {
278
- url: "https://raw.githubusercontent.com/cline/cline/main/apps/vscode/package.json",
279
- extract: clineClientVersion,
280
- },
248
+ { url: "https://raw.githubusercontent.com/cline/cline/main/apps/vscode/package.json" },
281
249
  ],
282
250
  },
283
251
  clineSdk: {
284
252
  key: "cline-sdk",
285
253
  fallback: "0.2.0",
286
- sources: [{ url: "https://registry.npmjs.org/@cline/sdk/latest", extract: clineSdkVersion }],
254
+ sources: [{ url: "https://registry.npmjs.org/@cline/sdk/latest" }],
287
255
  },
288
256
  clineCli: {
289
257
  key: "cline-cli",
290
258
  fallback: "3.0.74",
291
- sources: [{ url: "https://registry.npmjs.org/cline/latest", extract: clineSdkVersion }],
259
+ sources: [{ url: "https://registry.npmjs.org/cline/latest" }],
292
260
  },
293
261
  codex: {
294
262
  key: "codex",
295
- fallback: "0.167.0",
263
+ fallback: "0.167.1",
296
264
  sources: [{ url: "https://registry.npmjs.org/@openai/codex/latest" }],
297
265
  },
298
266
  kimiCli: {
299
267
  key: "kimi-cli",
300
268
  fallback: "1.52.0",
301
- sources: [{ url: "https://pypi.org/pypi/kimi-cli/json", extract: kimiVersion }],
269
+ sources: [{ url: "https://pypi.org/pypi/kimi-cli/json" }],
302
270
  },
303
271
  kiro: {
304
272
  key: "kiro",
305
- fallback: "1.4.121",
273
+ fallback: "1.4.154",
306
274
  sources: [
307
275
  {
308
276
  // Kiro's downloads page exposes no stable version string. Homebrew
309
277
  // tracks the official installer URL and exposes its version as JSON;
310
278
  // keep the product page parser as a fallback.
311
279
  url: "https://formulae.brew.sh/api/cask/kiro.json",
312
- extract: kiroCaskVersion,
313
280
  },
314
281
  { url: "https://kiro.dev/downloads/", extract: kiroVersion },
315
282
  ],
316
283
  },
317
284
  codebuddy: {
318
285
  key: "codebuddy",
319
- fallback: "2.181.0",
286
+ fallback: "2.183.0",
320
287
  sources: [
321
288
  { url: "https://registry.npmjs.org/@tencent-ai/codebuddy-code/latest" },
322
289
  { url: "https://registry.npmmirror.com/@tencent-ai/codebuddy-code/latest" },
@@ -324,7 +291,7 @@ export const VERSION_SOURCES = {
324
291
  },
325
292
  codebuddyCn: {
326
293
  key: "codebuddy-cn",
327
- fallback: "4.12.3.42658816",
294
+ fallback: "4.12.4.43368774",
328
295
  sources: [
329
296
  {
330
297
  // Official CN builds are listed by Homebrew's maintained cask feed;
@@ -339,7 +306,7 @@ export const VERSION_SOURCES = {
339
306
  // Current `@anthropic-ai/claude-code` release, so the billing
340
307
  // `cc_version=` suffix and the `claude-cli/` User-Agent stay on what
341
308
  // upstream ships.
342
- fallback: "2.1.319",
309
+ fallback: "2.1.322",
343
310
  sources: [{ url: "https://registry.npmjs.org/@anthropic-ai/claude-code/latest" }],
344
311
  },
345
312
  claudeSdk: {
@@ -366,21 +333,22 @@ export const VERSION_SOURCES = {
366
333
  },
367
334
  kilo: {
368
335
  key: "kilo-code",
369
- fallback: "7.8.27",
336
+ fallback: "7.8.28",
370
337
  sources: [{ url: "https://open-vsx.org/api/kilocode/kilo-code" }],
371
338
  },
372
339
  muse: {
373
340
  key: "muse-code",
374
- fallback: "1.4.14-R7309.1",
341
+ fallback: "1.4.15-R8339.1",
375
342
  sources: [{ url: "https://api.meta.ai/muse-code/channels/muse-stable" }],
376
343
  },
377
344
  trae: {
378
345
  key: "trae",
379
- // Fallback = the newest release the changelog names, which is the *high*
380
- // end of its newest line ("TRAE v3.6.1 ~ 3.6.4"). Discovery replaces it
381
- // within one monitor tick; it is only what a cold process sends before
382
- // the first successful fetch.
383
- fallback: "3.6.4",
346
+ // Fallback = the current desktop release per the official winget manifest
347
+ // (ByteDance.Trae 3.6.13). The docs changelog renders as an SPA and no
348
+ // longer exposes its newest entries to the parser below, so it is not a
349
+ // reliable discovery source right now — raise this pin when the desktop
350
+ // build moves.
351
+ fallback: "3.6.13",
384
352
  sources: [
385
353
  {
386
354
  url: "https://docs.trae.ai/ide/changelog?_lang=en",
@@ -391,24 +359,63 @@ export const VERSION_SOURCES = {
391
359
  freepi: {
392
360
  key: "freepi",
393
361
  // The free-pi CLI version — the `x-client-version` the gateway reports.
394
- // Fallback = the unpacked 0.6.14 bundle; npm discovery replaces it.
395
- fallback: "0.6.14",
362
+ // Fallback = the current npm latest (0.6.19 as of 2026-11-12); a cold
363
+ // process sends it until the monitor's first successful discovery.
364
+ fallback: "0.6.19",
396
365
  sources: [{ url: "https://registry.npmjs.org/free-pi-cli/latest" }],
397
366
  },
398
367
  perch: {
399
368
  key: "perch",
400
369
  // The official Perch CLI, spawned by the adapter for hosted inference.
401
- fallback: "2.4.125",
370
+ // Fallback = the current npm latest (2.4.128 as of 2026-11-11); a cold
371
+ // process sends it until the monitor's first successful discovery.
372
+ fallback: "2.4.128",
402
373
  sources: [{ url: "https://registry.npmjs.org/perchai-cli/latest" }],
403
374
  },
375
+ clixad: {
376
+ key: "clixad",
377
+ // The Clixad gateway publishes the CLI build it is paired with as
378
+ // `cli_latest` on `/v1/models` (unauthenticated). Fallback = the version
379
+ // the 0.0.1-beta.50 bundle shipped (verified live 2026-11-11); a cold
380
+ // process sends it until the monitor's first successful discovery.
381
+ fallback: "0.0.1-beta.50",
382
+ sources: [
383
+ {
384
+ url: "https://clixad.onrender.com/v1/models",
385
+ extract: clixadVersion,
386
+ },
387
+ ],
388
+ },
389
+ freebuff: {
390
+ key: "freebuff",
391
+ // No machine-readable public feed exists for Freebuff Desktop. GitHub
392
+ // releases only publish Codecane (a separate CLI line) staging builds,
393
+ // npm `freebuff` is the CLI (a different product), and the site's
394
+ // download links are client-rendered with no stable asset path.
395
+ // Fallback = the version unpacked from the official Windows installer
396
+ // during the wire audit (2026-11-09, Freebuff Desktop 0.0.201). Raise it
397
+ // when the next installer bundle is audited.
398
+ fallback: "0.0.201",
399
+ sources: [],
400
+ },
401
+ deepai: {
402
+ key: "deepai",
403
+ // No machine-readable public feed exists for the DeepAI Code desktop app
404
+ // (macOS/Windows only, no npm/CLI line). Fallback = the version unpacked
405
+ // from the audited bundle (2026-11-11, DeepAI Code 0.3.35). Raise it
406
+ // when the next bundle is audited. Pinned-only by design for the same
407
+ // reason as `freebuff`.
408
+ fallback: "0.3.35",
409
+ sources: [],
410
+ },
404
411
  cursor: {
405
412
  key: "cursor",
406
- // Fallback = the last observed wire version (CLI ~2026.11.07-306d153).
407
- // Discovery replaces it within one monitor tick; it is only what a cold
408
- // process sends before the first successful fetch. Kept equal to
409
- // `FALLBACK_CLIENT_VERSION` in the cursor adapter so the badge and the
410
- // request header always send one identity.
411
- fallback: "cli-3.29.9-306d153",
413
+ // Fallback = the newest stable build from the official download API
414
+ // (cli-3.29.15-86776fc). Discovery replaces it within one monitor tick; it
415
+ // is only what a cold process sends before the first successful fetch.
416
+ // Kept equal to `FALLBACK_CLIENT_VERSION` in the cursor adapter so the
417
+ // badge and the request header always send one identity.
418
+ fallback: "cli-3.29.15-86776fc",
412
419
  sources: [
413
420
  {
414
421
  url: "https://cursor.com/api/download?platform=win32-x64-user&releaseTrack=stable",
@@ -422,7 +429,7 @@ export const VERSION_SOURCES = {
422
429
  // comment this replaced claimed no semver existed and pinned a date tag
423
430
  // (`v2026.9.24`) as the version — a tag, not a release, and not the
424
431
  // identity the client sends.
425
- fallback: "0.23.0",
432
+ fallback: "0.23.1",
426
433
  sources: [
427
434
  {
428
435
  url: "https://api.github.com/repos/NousResearch/hermes-agent/releases/latest",
@@ -449,6 +456,9 @@ const resolvers = {
449
456
  trae: createClientVersionResolver({ ...VERSION_SOURCES.trae, client: "trae" }),
450
457
  freepi: createClientVersionResolver({ ...VERSION_SOURCES.freepi, client: "freepi" }),
451
458
  perch: createClientVersionResolver({ ...VERSION_SOURCES.perch, client: "perch" }),
459
+ clixad: createClientVersionResolver({ ...VERSION_SOURCES.clixad, client: "clixad" }),
460
+ freebuff: createClientVersionResolver({ ...VERSION_SOURCES.freebuff, client: "freebuff" }),
461
+ deepai: createClientVersionResolver({ ...VERSION_SOURCES.deepai, client: "deepai" }),
452
462
  cursor: createClientVersionResolver({ ...VERSION_SOURCES.cursor, client: "cursor" }),
453
463
  opencode: createClientVersionResolver({ ...VERSION_SOURCES.opencode, client: "opencode" }),
454
464
  commandcode: createClientVersionResolver({ ...VERSION_SOURCES.commandcode, client: "command-code" }),
@@ -470,6 +480,11 @@ const resolvers = {
470
480
  } satisfies Record<keyof typeof VERSION_SOURCES, ClientVersionResolver>;
471
481
  const PROVIDER_VERSION_RESOLVERS: Readonly<Record<string, ClientVersionResolver>> = {
472
482
  antigravity: resolvers.antigravity,
483
+ freepi: resolvers.freepi,
484
+ perch: resolvers.perch,
485
+ clixad: resolvers.clixad,
486
+ freebuff: resolvers.freebuff,
487
+ deepai: resolvers.deepai,
473
488
  grok: resolvers.grok,
474
489
  xai: resolvers.grok,
475
490
  opencode: resolvers.opencode,
@@ -583,6 +598,10 @@ export const getQoderVersion = accessor("qoder").get;
583
598
  export const getHermesVersion = accessor("hermes").get;
584
599
  export const getTraeVersion = accessor("trae").get;
585
600
  export const getAntigravityVersion = accessor("antigravity").get;
601
+ export const getFreepiVersion = accessor("freepi").get;
602
+ export const getPerchVersion = accessor("perch").get;
603
+ export const getFreebuffVersion = accessor("freebuff").get;
604
+ export const getDeepAIVersion = accessor("deepai").get;
586
605
 
587
606
  export const getOpenCodeVersion = accessor("opencode").get;
588
607
 
@@ -28,24 +28,19 @@ type ProviderRules = Record<string, CategoryRules>;
28
28
  const DAY = 24 * 60 * 60 * 1000;
29
29
  const MINUTE = 60 * 1000;
30
30
 
31
- const COOLDOWN_RULES: ProviderRules = {
32
- trae: {
33
- model_unreachable: {
34
- free: DAY,
35
- paid: 15 * MINUTE,
36
- pro: 15 * MINUTE,
37
- },
31
+ const TRAE_RULES: CategoryRules = {
32
+ model_unreachable: {
33
+ free: DAY,
34
+ paid: 15 * MINUTE,
35
+ pro: 15 * MINUTE,
38
36
  },
37
+ };
38
+
39
+ const COOLDOWN_RULES: ProviderRules = {
40
+ trae: TRAE_RULES,
39
41
  // Trae CN is the same upstream product under a second realm id — the
40
- // classifier already treats the two identically (trae-region-unavailable,
41
- // trae-quota-exhausted), so the rules mirror.
42
- traecn: {
43
- model_unreachable: {
44
- free: DAY,
45
- paid: 15 * MINUTE,
46
- pro: 15 * MINUTE,
47
- },
48
- },
42
+ // classifier already treats the two identically, so the rules alias.
43
+ traecn: TRAE_RULES,
49
44
  };
50
45
 
51
46
  /**