cartethyia 2.4.5 → 2.5.0

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 (415) hide show
  1. package/.env.example +4 -1
  2. package/README.md +63 -13
  3. package/dist/dashboard/assets/AccountCooldown-ChpdzxRH.js.gz +0 -0
  4. package/dist/dashboard/assets/BannedPage-9igfctfZ.js +1 -0
  5. package/dist/dashboard/assets/BannedPage-9igfctfZ.js.gz +0 -0
  6. package/dist/dashboard/assets/{CliToolDetailPage-BzGevuql.js → CliToolDetailPage-ZRr2K2sF.js} +1 -1
  7. package/dist/dashboard/assets/CliToolDetailPage-ZRr2K2sF.js.gz +0 -0
  8. package/dist/dashboard/assets/{CliToolsPage-BaRM_7We.js → CliToolsPage-D3xqZbN-.js} +1 -1
  9. package/dist/dashboard/assets/CliToolsPage-D3xqZbN-.js.gz +0 -0
  10. package/dist/dashboard/assets/CombosPage-CVnQPBBz.js +1 -0
  11. package/dist/dashboard/assets/CombosPage-CVnQPBBz.js.gz +0 -0
  12. package/dist/dashboard/assets/ConsoleLogPage-u6W1UXSc.js +1 -0
  13. package/dist/dashboard/assets/ConsoleLogPage-u6W1UXSc.js.gz +0 -0
  14. package/dist/dashboard/assets/{CustomizationPage-DqUGcoH0.js → CustomizationPage-CTTpCb25.js} +1 -1
  15. package/dist/dashboard/assets/CustomizationPage-CTTpCb25.js.gz +0 -0
  16. package/dist/dashboard/assets/HealthEventsModal-C5XpWlvy.js +1 -0
  17. package/dist/dashboard/assets/HealthEventsModal-C5XpWlvy.js.gz +0 -0
  18. package/dist/dashboard/assets/{LoginPage-DbhTGfhb.js → LoginPage-DHoB3Agn.js} +1 -1
  19. package/dist/dashboard/assets/LoginPage-DHoB3Agn.js.gz +0 -0
  20. package/dist/dashboard/assets/ModelPicker-oQ-SDXp8.js +1 -0
  21. package/dist/dashboard/assets/ModelPicker-oQ-SDXp8.js.gz +0 -0
  22. package/dist/dashboard/assets/OverviewPage-BrY4SgIt.js +1 -0
  23. package/dist/dashboard/assets/OverviewPage-BrY4SgIt.js.gz +0 -0
  24. package/dist/dashboard/assets/ProviderDetailPage-D9VSt7T_.js +3 -0
  25. package/dist/dashboard/assets/ProviderDetailPage-D9VSt7T_.js.gz +0 -0
  26. package/dist/dashboard/assets/ProviderIcon-CmfzxpVM.js +1 -0
  27. package/dist/dashboard/assets/ProviderIcon-CmfzxpVM.js.gz +0 -0
  28. package/dist/dashboard/assets/ProvidersPage-Df77V5I5.js +1 -0
  29. package/dist/dashboard/assets/ProvidersPage-Df77V5I5.js.gz +0 -0
  30. package/dist/dashboard/assets/ProxyPage-CDfxP7AC.js +7 -0
  31. package/dist/dashboard/assets/ProxyPage-CDfxP7AC.js.gz +0 -0
  32. package/dist/dashboard/assets/QuotaPage-CY4o2t4p.js +1 -0
  33. package/dist/dashboard/assets/QuotaPage-CY4o2t4p.js.gz +0 -0
  34. package/dist/dashboard/assets/{SettingsPage-YxRml8AT.js → SettingsPage-CvjZxHE4.js} +1 -1
  35. package/dist/dashboard/assets/SettingsPage-CvjZxHE4.js.gz +0 -0
  36. package/dist/dashboard/assets/SetupPage-D3nza7LH.js +1 -0
  37. package/dist/dashboard/assets/SetupPage-D3nza7LH.js.gz +0 -0
  38. package/dist/dashboard/assets/{SortableList-Bol1ASmI.js → SortableList-Dtt12WT1.js} +1 -1
  39. package/dist/dashboard/assets/SortableList-Dtt12WT1.js.gz +0 -0
  40. package/dist/dashboard/assets/StudioPage-CbK3hXvU.js +31 -0
  41. package/dist/dashboard/assets/StudioPage-CbK3hXvU.js.gz +0 -0
  42. package/dist/dashboard/assets/ToolIcon-K1ckcDKg.js.gz +0 -0
  43. package/dist/dashboard/assets/UsagePage-BQlWbWg2.js +56 -0
  44. package/dist/dashboard/assets/UsagePage-BQlWbWg2.js.gz +0 -0
  45. package/dist/dashboard/assets/api-keys-BmDbsior.js +1 -0
  46. package/dist/dashboard/assets/api-keys-BmDbsior.js.gz +0 -0
  47. package/dist/dashboard/assets/app-BbpSarVV.css +2 -0
  48. package/dist/dashboard/assets/app-BbpSarVV.css.gz +0 -0
  49. package/dist/dashboard/assets/{app-BDJWMThu.js → app-C-Y4oZT0.js} +3 -3
  50. package/dist/dashboard/assets/app-C-Y4oZT0.js.gz +0 -0
  51. package/dist/dashboard/assets/arrow-left-CpBxkoxI.js.gz +0 -0
  52. package/dist/dashboard/assets/arrow-right-Dl5yKnp8.js.gz +0 -0
  53. package/dist/dashboard/assets/assert-BHfaei-X.js +1 -0
  54. package/dist/dashboard/assets/assert-BHfaei-X.js.gz +0 -0
  55. package/dist/dashboard/assets/backup-file-gdKIlg8U.js.gz +0 -0
  56. package/dist/dashboard/assets/{badge-BwmwEOgA.js → badge-DpXT4Ixt.js} +1 -1
  57. package/dist/dashboard/assets/badge-DpXT4Ixt.js.gz +0 -0
  58. package/dist/dashboard/assets/{banner-BKBfKTqn.js → banner-CSAAWLJv.js} +1 -1
  59. package/dist/dashboard/assets/banner-CSAAWLJv.js.gz +0 -0
  60. package/dist/dashboard/assets/brain-Cwed_72z.js.gz +0 -0
  61. package/dist/dashboard/assets/{card-BTdbRFFg.js → card-DEhRXXw4.js} +1 -1
  62. package/dist/dashboard/assets/card-DEhRXXw4.js.gz +0 -0
  63. package/dist/dashboard/assets/check-D69FzdvC.js.gz +0 -0
  64. package/dist/dashboard/assets/chevron-right-BXIy5l3h.js.gz +0 -0
  65. package/dist/dashboard/assets/circle-alert-CntYHV5v.js.gz +0 -0
  66. package/dist/dashboard/assets/circle-check-BU3v_mFW.js.gz +0 -0
  67. package/dist/dashboard/assets/cli-tools-B0wBq-RL.js +1 -0
  68. package/dist/dashboard/assets/cli-tools-B0wBq-RL.js.gz +0 -0
  69. package/dist/dashboard/assets/clipboard-button-DOo6EU8O.js +1 -0
  70. package/dist/dashboard/assets/clipboard-button-DOo6EU8O.js.gz +0 -0
  71. package/dist/dashboard/assets/cog-CdWhsajs.js.gz +0 -0
  72. package/dist/dashboard/assets/copy-ngmQ9zcZ.js.gz +0 -0
  73. package/dist/dashboard/assets/{dialog-iocKA3jr.js → dialog-BvQeBMfP.js} +1 -1
  74. package/dist/dashboard/assets/dialog-BvQeBMfP.js.gz +0 -0
  75. package/dist/dashboard/assets/dialog-actions-B1axpEg7.js.gz +0 -0
  76. package/dist/dashboard/assets/download-A0SW9OCW.js.gz +0 -0
  77. package/dist/dashboard/assets/download-dRFo5_Wu.js.gz +0 -0
  78. package/dist/dashboard/assets/{drawer-CDKOTZdY.js → drawer-CMwPXwQB.js} +1 -1
  79. package/dist/dashboard/assets/drawer-CMwPXwQB.js.gz +0 -0
  80. package/dist/dashboard/assets/entry-D4O2229X.js +1 -0
  81. package/dist/dashboard/assets/entry-D4O2229X.js.gz +0 -0
  82. package/dist/dashboard/assets/entry-DKvr5Q9K.js +1 -0
  83. package/dist/dashboard/assets/entry-DKvr5Q9K.js.gz +0 -0
  84. package/dist/dashboard/assets/entry-FQCejoDY.js +4 -0
  85. package/dist/dashboard/assets/entry-FQCejoDY.js.gz +0 -0
  86. package/dist/dashboard/assets/eye-C_hawfH4.js.gz +0 -0
  87. package/dist/dashboard/assets/eye-off-Bk5g2luF.js.gz +0 -0
  88. package/dist/dashboard/assets/fingerprint-pattern-BWuHtbW4.js.gz +0 -0
  89. package/dist/dashboard/assets/gauge-BbIMy7MB.js.gz +0 -0
  90. package/dist/dashboard/assets/github-badge-zliLvBwr.js +1 -0
  91. package/dist/dashboard/assets/github-badge-zliLvBwr.js.gz +0 -0
  92. package/dist/dashboard/assets/image-BPaouViG.js.gz +0 -0
  93. package/dist/dashboard/assets/{index-C4hXfAMs.js → index-CXm3FIyt.js} +2 -2
  94. package/dist/dashboard/assets/index-CXm3FIyt.js.gz +0 -0
  95. package/dist/dashboard/assets/{inline-DCejSLlA.js → inline-D1IlZZ8H.js} +1 -1
  96. package/dist/dashboard/assets/inline-D1IlZZ8H.js.gz +0 -0
  97. package/dist/dashboard/assets/{input-Bn4_kNav.js → input-BpVZdrbc.js} +1 -1
  98. package/dist/dashboard/assets/input-BpVZdrbc.js.gz +0 -0
  99. package/dist/dashboard/assets/jsx-runtime-CXUMngI_.js.gz +0 -0
  100. package/dist/dashboard/assets/key-round-UkavsGxp.js.gz +0 -0
  101. package/dist/dashboard/assets/layers-2-glBbjl.js.gz +0 -0
  102. package/dist/dashboard/assets/{layout-Bv-NxqZ5.js → layout-BioLW7gN.js} +1 -1
  103. package/dist/dashboard/assets/layout-BioLW7gN.js.gz +0 -0
  104. package/dist/dashboard/assets/{list-row-BLA0sldD.js → list-row-CwVcGAIc.js} +1 -1
  105. package/dist/dashboard/assets/list-row-CwVcGAIc.js.gz +0 -0
  106. package/dist/dashboard/assets/{live-YlifPYox.js → live-yfEJFF9T.js} +1 -1
  107. package/dist/dashboard/assets/live-yfEJFF9T.js.gz +0 -0
  108. package/dist/dashboard/assets/log-in-BmRjy60c.js.gz +0 -0
  109. package/dist/dashboard/assets/message-circle-CCxGwRvv.js.gz +0 -0
  110. package/dist/dashboard/assets/model-bans-CT-TxSNX.js +1 -0
  111. package/dist/dashboard/assets/model-bans-CT-TxSNX.js.gz +0 -0
  112. package/dist/dashboard/assets/mutation-BkoLM5Ba.js +1 -0
  113. package/dist/dashboard/assets/mutation-BkoLM5Ba.js.gz +0 -0
  114. package/dist/dashboard/assets/numbers-CFL4_Dqc.js.gz +0 -0
  115. package/dist/dashboard/assets/pencil-768ysRAr.js.gz +0 -0
  116. package/dist/dashboard/assets/plus-DIdX_g-x.js.gz +0 -0
  117. package/dist/dashboard/assets/power-off-Yjw9GMJh.js +1 -0
  118. package/dist/dashboard/assets/power-off-Yjw9GMJh.js.gz +0 -0
  119. package/dist/dashboard/assets/privacy-mode-GM8bej85.js +1 -0
  120. package/dist/dashboard/assets/privacy-mode-GM8bej85.js.gz +0 -0
  121. package/dist/dashboard/assets/providers/agentrouter.svg.gz +0 -0
  122. package/dist/dashboard/assets/providers/aihubmix.svg.gz +0 -0
  123. package/dist/dashboard/assets/providers/alibaba-coding-plan.svg.gz +0 -0
  124. package/dist/dashboard/assets/providers/alibaba.svg.gz +0 -0
  125. package/dist/dashboard/assets/providers/antigravity.svg.gz +0 -0
  126. package/dist/dashboard/assets/providers/bai.svg.gz +0 -0
  127. package/dist/dashboard/assets/providers/blackbox.svg.gz +0 -0
  128. package/dist/dashboard/assets/providers/claude-code.svg.gz +0 -0
  129. package/dist/dashboard/assets/providers/cursor.webp +0 -0
  130. package/dist/dashboard/assets/providers/exa.svg.gz +0 -0
  131. package/dist/dashboard/assets/providers/gmi-cloud.svg.gz +0 -0
  132. package/dist/dashboard/assets/providers/inferhub.svg.gz +0 -0
  133. package/dist/dashboard/assets/providers/muse.svg.gz +0 -0
  134. package/dist/dashboard/assets/providers/nous-research.svg.gz +0 -0
  135. package/dist/dashboard/assets/providers/openai-light.svg.gz +0 -0
  136. package/dist/dashboard/assets/providers/parallel.svg.gz +0 -0
  137. package/dist/dashboard/assets/providers/tokenharbor.svg.gz +0 -0
  138. package/dist/dashboard/assets/providers/trae.webp +0 -0
  139. package/dist/dashboard/assets/providers/zai-coding-plan.svg.gz +0 -0
  140. package/dist/dashboard/assets/{query-keys-ROf-g1Wi.js → query-keys-D7wX2vM5.js} +1 -1
  141. package/dist/dashboard/assets/query-keys-D7wX2vM5.js.gz +0 -0
  142. package/dist/dashboard/assets/quota-DZGyp0N5.js.gz +0 -0
  143. package/dist/dashboard/assets/refresh-cw-BqJFk4sU.js.gz +0 -0
  144. package/dist/dashboard/assets/rotate-ccw-CufcqITi.js.gz +0 -0
  145. package/dist/dashboard/assets/route-CNSNj9Zm.js.gz +0 -0
  146. package/dist/dashboard/assets/routing-Cs6jl49L.js +1 -0
  147. package/dist/dashboard/assets/routing-Cs6jl49L.js.gz +0 -0
  148. package/dist/dashboard/assets/scaling-nWE4Cx8f.js.gz +0 -0
  149. package/dist/dashboard/assets/search-JOKus9rb.js.gz +0 -0
  150. package/dist/dashboard/assets/select-C0Rer3gF.js +41 -0
  151. package/dist/dashboard/assets/select-C0Rer3gF.js.gz +0 -0
  152. package/dist/dashboard/assets/settings-CfcTcOyU.js +1 -0
  153. package/dist/dashboard/assets/settings-CfcTcOyU.js.gz +0 -0
  154. package/dist/dashboard/assets/shield-check-DVS0Foqh.js.gz +0 -0
  155. package/dist/dashboard/assets/sparkles-BRaRUxAU.js.gz +0 -0
  156. package/dist/dashboard/assets/{stack-C1kTcOmP.js → stack-C9_uUjaA.js} +1 -1
  157. package/dist/dashboard/assets/stack-C9_uUjaA.js.gz +0 -0
  158. package/dist/dashboard/assets/{state-DHKdg-pm.js → state-DA0B95UQ.js} +1 -1
  159. package/dist/dashboard/assets/state-DA0B95UQ.js.gz +0 -0
  160. package/dist/dashboard/assets/stream-retry-5QnVuSSo.js.gz +0 -0
  161. package/dist/dashboard/assets/{switch-DIDeAiz9.js → switch-Cittxy-x.js} +1 -1
  162. package/dist/dashboard/assets/switch-Cittxy-x.js.gz +0 -0
  163. package/dist/dashboard/assets/terminal-Bd4ee8TA.js +1 -0
  164. package/dist/dashboard/assets/terminal-Bd4ee8TA.js.gz +0 -0
  165. package/dist/dashboard/assets/toast-CNt_jzO4.js +2 -0
  166. package/dist/dashboard/assets/toast-CNt_jzO4.js.gz +0 -0
  167. package/dist/dashboard/assets/tokens-DDxMbSRL.js.gz +0 -0
  168. package/dist/dashboard/assets/trash-Cppn5c3R.js.gz +0 -0
  169. package/dist/dashboard/assets/use-clipboard-B0Sy03fE.js.gz +0 -0
  170. package/dist/dashboard/assets/use-presence-BSZEBx7U.js.gz +0 -0
  171. package/dist/dashboard/assets/use-reduced-motion-C4B0R6Sz.js.gz +0 -0
  172. package/dist/dashboard/assets/use-timeout-BwcBBRrT.js.gz +0 -0
  173. package/dist/dashboard/assets/{useMutation-DVmwOKU-.js → useMutation-BQKMcJ1H.js} +1 -1
  174. package/dist/dashboard/assets/useMutation-BQKMcJ1H.js.gz +0 -0
  175. package/dist/dashboard/assets/wrench-B6E1oEuY.js.gz +0 -0
  176. package/dist/dashboard/index.html +1 -1
  177. package/dist/dashboard/index.html.gz +0 -0
  178. package/dist/dashboard/precompressed-assets.json +122 -0
  179. package/migrations/full-postgres/0013_add_providers_tenant_id_index.sql +22 -0
  180. package/migrations/full-postgres/0014_add_provider_accounts_tenant_id_index.sql +21 -0
  181. package/migrations/full-postgres/0015_add_payload_stages.sql +18 -0
  182. package/migrations/full-postgres/0016_add_share_link_slug.sql +20 -0
  183. package/migrations/full-postgres/0017_add_models_pro_only.sql +18 -0
  184. package/migrations/full-postgres/0018_add_accounts_quota_plan.sql +18 -0
  185. package/migrations/full-postgres/0019_add_models_free_tier.sql +15 -0
  186. package/migrations/lite-sqlite3/0000_baseline.sql +4 -2
  187. package/migrations/lite-sqlite3/0013_add_providers_tenant_id_index.sql +4 -0
  188. package/migrations/lite-sqlite3/0014_add_provider_accounts_tenant_id_index.sql +4 -0
  189. package/migrations/lite-sqlite3/0015_add_payload_stages.sql +18 -0
  190. package/migrations/lite-sqlite3/0016_add_share_link_slug.sql +20 -0
  191. package/migrations/lite-sqlite3/0017_add_models_pro_only.sql +18 -0
  192. package/migrations/lite-sqlite3/0018_add_accounts_quota_plan.sql +18 -0
  193. package/migrations/lite-sqlite3/0019_add_models_free_tier.sql +15 -0
  194. package/package.json +26 -30
  195. package/scripts/cli/{carte-launcher.cjs → bin/carte.cjs} +8 -8
  196. package/scripts/cli/{cli-launcher.cjs → bin/cartethyia.cjs} +167 -134
  197. package/scripts/cli/command/admin.ts +65 -0
  198. package/scripts/cli/command/console-menu.ts +246 -0
  199. package/scripts/cli/{doctor.ts → command/doctor.ts} +257 -258
  200. package/scripts/cli/{setup.ts → command/setup.ts} +8 -12
  201. package/scripts/cli/command/start.ts +77 -0
  202. package/scripts/cli/command/tray.ts +165 -0
  203. package/scripts/cli/command/update.ts +23 -0
  204. package/scripts/cli/{runtime-state.ts → gateway/drain-token.ts} +78 -78
  205. package/scripts/cli/gateway/instance-lock.ts +105 -0
  206. package/scripts/cli/gateway/spawn.ts +173 -0
  207. package/scripts/cli/gateway/stop.ts +51 -0
  208. package/scripts/cli/main.ts +94 -0
  209. package/scripts/cli/support/bun.ts +27 -0
  210. package/scripts/cli/support/env.ts +99 -0
  211. package/scripts/cli/support/platform.ts +64 -0
  212. package/scripts/cli/support/services.ts +88 -0
  213. package/scripts/cli/support/startup-signal.ts +23 -0
  214. package/scripts/cli/{version.ts → support/version.ts} +1 -1
  215. package/scripts/cli/tray/hidden-window.vbs +28 -0
  216. package/scripts/cli/{tray.ts → tray/index.ts} +3 -3
  217. package/scripts/cli/{tray.ps1 → tray/windows.ps1} +107 -98
  218. package/scripts/cli/ui/background.ts +110 -0
  219. package/src/boot/app.ts +23 -7
  220. package/src/boot/dependencies.ts +55 -13
  221. package/src/boot/elysia-typebox-bridge.ts +37 -0
  222. package/src/boot/lifecycle.ts +2 -0
  223. package/src/config/cooldowns.ts +138 -138
  224. package/src/config/providers.ts +31 -5
  225. package/src/config/runtime.ts +19 -3
  226. package/src/config/spec.ts +20 -0
  227. package/src/console/api-keys/contracts.ts +414 -391
  228. package/src/console/api-keys/routes.ts +880 -870
  229. package/src/console/auth/account-reset-store.ts +14 -0
  230. package/src/console/auth/account-reset.ts +3 -2
  231. package/src/console/auth/setup-service.ts +6 -3
  232. package/src/console/backup/routes.ts +8 -0
  233. package/src/console/cli-tools/contracts.ts +6 -6
  234. package/src/console/console-router.ts +5 -3
  235. package/src/console/dashboard-assets.ts +81 -11
  236. package/src/console/domain-registration.ts +9 -2
  237. package/src/console/observability/contracts.ts +475 -423
  238. package/src/console/observability/routes.ts +31 -6
  239. package/src/console/observability/store.ts +1015 -912
  240. package/src/console/providers/catalog/contracts.ts +31 -0
  241. package/src/console/providers/catalog/provider-operations.ts +8 -0
  242. package/src/console/providers/catalog/public-model-store.ts +91 -60
  243. package/src/console/providers/catalog/store.ts +1532 -1521
  244. package/src/console/providers/oauth/callback-listener.ts +18 -3
  245. package/src/console/providers/oauth/routes.ts +38 -15
  246. package/src/console/quota/account-quota-tenant.ts +9 -6
  247. package/src/console/quota/refresh.ts +19 -0
  248. package/src/console/response-compression.ts +69 -2
  249. package/src/console/routing/pools/probe-verdict.ts +72 -0
  250. package/src/console/share/share-router.ts +7 -4
  251. package/src/console/share/share-stats.ts +12 -5
  252. package/src/console/shared/read-through-cache.ts +133 -7
  253. package/src/console/updates/repository-updates.ts +39 -34
  254. package/src/console/updates/routes.ts +162 -4
  255. package/src/main.ts +3 -0
  256. package/src/network/http2-bidi.ts +85 -0
  257. package/src/network/outbound-fetch.ts +87 -24
  258. package/src/network/pool/agent.ts +478 -460
  259. package/src/network/pool/resolver.ts +1104 -544
  260. package/src/network/pool-health-machine.ts +12 -5
  261. package/src/observability/event-log.ts +29 -8
  262. package/src/observability/metrics.ts +40 -13
  263. package/src/observability/payload-capture.ts +344 -324
  264. package/src/observability/runtime-metrics.ts +7 -0
  265. package/src/persistence/db-pg.ts +9 -3
  266. package/src/persistence/db-sqlite.ts +497 -495
  267. package/src/persistence/health-event-flush.ts +137 -0
  268. package/src/persistence/migrate.ts +181 -129
  269. package/src/persistence/schema.ts +1360 -1257
  270. package/src/persistence/settings-revision.ts +25 -0
  271. package/src/persistence/shared-cache.ts +217 -0
  272. package/src/persistence/stores/share-store.ts +556 -528
  273. package/src/persistence/usage-flush.ts +61 -1
  274. package/src/protocol/canonical-model.ts +23 -1
  275. package/src/protocol/response/chat.ts +143 -10
  276. package/src/protocol/response/codex.ts +9 -75
  277. package/src/protocol/response/omp-tool-call-markup.ts +221 -0
  278. package/src/protocol/tool-identity.ts +43 -9
  279. package/src/providers/authentication/jwt-validator.ts +130 -187
  280. package/src/providers/authentication/oauth-flow-store.ts +16 -9
  281. package/src/providers/default-registry.ts +177 -1
  282. package/src/providers/discovery/catalog/catalog-format.ts +20 -0
  283. package/src/providers/discovery/models-dev-catalog.ts +39 -22
  284. package/src/providers/discovery/openai-model-discovery.ts +39 -3
  285. package/src/providers/discovery/openrouter-free-roster.ts +98 -0
  286. package/src/providers/discovery/probe-wire.ts +29 -5
  287. package/src/providers/discovery/probing-service.ts +1022 -1001
  288. package/src/providers/integrations/buddy/buddy-catalog-shared.ts +42 -2
  289. package/src/providers/integrations/buddy/buddy-chat-shared.ts +913 -914
  290. package/src/providers/integrations/buddy/buddy-cli-headers.ts +88 -0
  291. package/src/providers/integrations/buddy/buddy-conversation-headers.ts +22 -5
  292. package/src/providers/integrations/buddy/buddy-discovery-shared.ts +33 -1
  293. package/src/providers/integrations/buddy/buddy-identity.ts +46 -0
  294. package/src/providers/integrations/buddy/buddy-oauth-shared.ts +37 -1
  295. package/src/providers/integrations/buddy/buddy-usage-store.ts +40 -5
  296. package/src/providers/integrations/buddy/codebuddy/cn-provider.ts +4 -0
  297. package/src/providers/integrations/buddy/codebuddy/provider.ts +180 -175
  298. package/src/providers/integrations/buddy/codebuddy/shared.ts +115 -73
  299. package/src/providers/integrations/buddy/workbuddy/cn-provider.ts +5 -2
  300. package/src/providers/integrations/buddy/workbuddy/provider.ts +206 -204
  301. package/src/providers/integrations/buddy/workbuddy/quota.ts +172 -159
  302. package/src/providers/integrations/buddy/workbuddy/shared.ts +200 -171
  303. package/src/providers/integrations/claude/claude.ts +606 -582
  304. package/src/providers/integrations/cline/cline-quota.ts +18 -16
  305. package/src/providers/integrations/cline/cline.ts +539 -468
  306. package/src/providers/integrations/codex/codex.ts +5 -8
  307. package/src/providers/integrations/commandcode/commandcode.ts +675 -541
  308. package/src/providers/integrations/commandcode/discovery.ts +116 -0
  309. package/src/providers/integrations/commandcode/quota.ts +160 -0
  310. package/src/providers/integrations/cursor/cursor-auth.ts +418 -0
  311. package/src/providers/integrations/cursor/cursor-catalog.ts +368 -0
  312. package/src/providers/integrations/cursor/cursor-http2.ts +300 -0
  313. package/src/providers/integrations/cursor/cursor-identity.ts +92 -0
  314. package/src/providers/integrations/cursor/cursor-request.ts +471 -0
  315. package/src/providers/integrations/cursor/cursor-session-manager.ts +281 -0
  316. package/src/providers/integrations/cursor/cursor-shared.ts +108 -0
  317. package/src/providers/integrations/cursor/cursor.ts +960 -0
  318. package/src/providers/integrations/cursor/protocol/framing.ts +129 -0
  319. package/src/providers/integrations/cursor/protocol/messages.ts +2355 -0
  320. package/src/providers/integrations/cursor/protocol/struct.ts +305 -0
  321. package/src/providers/integrations/factory/factory-oauth.ts +7 -11
  322. package/src/providers/integrations/github/github.ts +4 -4
  323. package/src/providers/integrations/hermes.ts +79 -8
  324. package/src/providers/integrations/kilo/kilo-discovery.ts +10 -1
  325. package/src/providers/integrations/kilo/kilo.ts +153 -146
  326. package/src/providers/integrations/opencode/opencode-go-quota.ts +130 -0
  327. package/src/providers/integrations/opencode/provider.ts +532 -499
  328. package/src/providers/integrations/openrouter/provider.ts +139 -281
  329. package/src/providers/integrations/qoder/qoder-catalog.ts +450 -0
  330. package/src/providers/integrations/qoder/qoder-oauth.ts +310 -263
  331. package/src/providers/integrations/qoder/qoder-realm.ts +106 -0
  332. package/src/providers/integrations/qoder/qoder-virtual.ts +77 -0
  333. package/src/providers/integrations/qoder/qoder.ts +865 -730
  334. package/src/providers/integrations/shared/install-id.ts +29 -2
  335. package/src/providers/integrations/trae/trae-catalog.ts +190 -0
  336. package/src/providers/integrations/trae/trae-headers.ts +149 -0
  337. package/src/providers/integrations/trae/trae-oauth.ts +773 -0
  338. package/src/providers/integrations/trae/trae-protocol.ts +400 -0
  339. package/src/providers/integrations/trae/trae-realm.ts +220 -0
  340. package/src/providers/integrations/trae/trae.ts +458 -0
  341. package/src/providers/operations/account-health-service.ts +107 -10
  342. package/src/providers/operations/client-versions.ts +143 -6
  343. package/src/providers/operations/model-catalog-cache.ts +18 -3
  344. package/src/providers/operations/provider-catalog-seeder.ts +95 -86
  345. package/src/providers/operations/provider-catalog-service.ts +76 -0
  346. package/src/providers/operations/provider-credential-service.ts +46 -7
  347. package/src/providers/provider-contracts.ts +76 -0
  348. package/src/providers/provider-metadata.ts +78 -6
  349. package/src/providers/provider-model-definition.ts +373 -349
  350. package/src/security/api-key-auth.ts +126 -8
  351. package/src/transport/contracts.ts +3 -0
  352. package/src/transport/dispatch/attempt-finalize.ts +39 -2
  353. package/src/transport/dispatch/attempt-loop.ts +530 -513
  354. package/src/transport/dispatch/empty-stream.ts +17 -26
  355. package/src/transport/dispatch/generation.ts +88 -21
  356. package/src/transport/dispatch/leases.ts +99 -2
  357. package/src/transport/dispatch/proxy-request.ts +60 -3
  358. package/src/transport/dispatch/streaming/finalize.ts +16 -1
  359. package/src/transport/dispatch/streaming/stream.ts +22 -2
  360. package/src/transport/dispatch/upstream.ts +19 -1
  361. package/src/transport/failure-policy.ts +20 -0
  362. package/src/transport/guards/auth-guard.ts +26 -1
  363. package/src/transport/guards/body-policy.ts +5 -4
  364. package/src/transport/request/preparer.ts +103 -0
  365. package/src/transport/request/state.ts +14 -0
  366. package/src/transport/routing/plan-gate.ts +45 -0
  367. package/src/transport/routing/route-catalog.ts +799 -738
  368. package/src/transport/routing/route-model.ts +52 -1
  369. package/src/transport/routing/router.ts +51 -0
  370. package/src/transport/surface/chat/encode.ts +18 -31
  371. package/src/transport/surface/messages/stream.ts +3 -2
  372. package/src/transport/translation/tool-repair.ts +37 -16
  373. package/src/workers/account-identity-sweep.ts +14 -12
  374. package/src/workers/daily-checkin.ts +62 -0
  375. package/src/workers/quota-refresh-sweep.ts +27 -2
  376. package/dist/dashboard/assets/BannedPage-DUrXpp-X.js +0 -1
  377. package/dist/dashboard/assets/CombosPage-B7ETkfVH.js +0 -1
  378. package/dist/dashboard/assets/ConsoleLogPage-AisQzCI1.js +0 -1
  379. package/dist/dashboard/assets/HealthEventsModal-BNflDm_f.js +0 -1
  380. package/dist/dashboard/assets/ModelPicker-CAiPR5Vd.js +0 -1
  381. package/dist/dashboard/assets/OverviewPage-C3NIzsDx.js +0 -1
  382. package/dist/dashboard/assets/ProviderDetailPage-BK8i5B5X.js +0 -3
  383. package/dist/dashboard/assets/ProviderIcon-paOCq8I0.js +0 -1
  384. package/dist/dashboard/assets/ProvidersPage-CvCVgm1e.js +0 -1
  385. package/dist/dashboard/assets/ProxyPage-DjabkuzP.js +0 -7
  386. package/dist/dashboard/assets/QuotaPage-Dxe0B0iu.js +0 -1
  387. package/dist/dashboard/assets/SetupPage-BaD4P_jW.js +0 -1
  388. package/dist/dashboard/assets/StudioPage-CLP4le3U.js +0 -31
  389. package/dist/dashboard/assets/UsagePage-Djes-Epy.js +0 -56
  390. package/dist/dashboard/assets/api-keys-CjIGQhe7.js +0 -1
  391. package/dist/dashboard/assets/app-Cjgo4-Lf.css +0 -2
  392. package/dist/dashboard/assets/assert-Bwh6DJTK.js +0 -1
  393. package/dist/dashboard/assets/cli-tools-DccaOfhX.js +0 -1
  394. package/dist/dashboard/assets/clipboard-button-DQmMikNQ.js +0 -1
  395. package/dist/dashboard/assets/entry-Cpi0rph1.js +0 -4
  396. package/dist/dashboard/assets/entry-Dm3PpDCO.js +0 -1
  397. package/dist/dashboard/assets/entry-keNDZ_Wz.js +0 -1
  398. package/dist/dashboard/assets/github-badge-BuAJr8oU.js +0 -1
  399. package/dist/dashboard/assets/model-bans-CVvHZ-qz.js +0 -1
  400. package/dist/dashboard/assets/mutation-BIizFZav.js +0 -1
  401. package/dist/dashboard/assets/privacy-mode-CQjbZ3xk.js +0 -1
  402. package/dist/dashboard/assets/routing-jntw9-Ui.js +0 -1
  403. package/dist/dashboard/assets/select-CGUuTQmj.js +0 -41
  404. package/dist/dashboard/assets/settings-BAS6QbmJ.js +0 -1
  405. package/dist/dashboard/assets/sliders-horizontal-ClG7dBeJ.js +0 -1
  406. package/dist/dashboard/assets/terminal-C9mZ4Ugm.js +0 -1
  407. package/dist/dashboard/assets/toast-DKAdP2mo.js +0 -2
  408. package/scripts/cli/cli.ts +0 -771
  409. package/scripts/operations/probe-egress.ts +0 -335
  410. package/scripts/operations/restart.ts +0 -120
  411. package/scripts/shared/env.ts +0 -269
  412. /package/{tray-assets → scripts/cli/tray/assets}/cartethyia.ico +0 -0
  413. /package/{tray-assets → scripts/cli/tray/assets}/cartethyia.png +0 -0
  414. /package/scripts/cli/{menu.ts → ui/prompt.ts} +0 -0
  415. /package/scripts/cli/{wordmark.ts → ui/wordmark.ts} +0 -0
@@ -0,0 +1,88 @@
1
+ /**
2
+ * The header family only the CLI identity sends.
3
+ *
4
+ * Everything here comes from the two reference gateways, which agree: the
5
+ * desktop product sends conversation/tracing headers and browser transport,
6
+ * while the CLI sends the agent envelope plus the OpenAI Node SDK fingerprint
7
+ * and no browser headers at all. A request carrying both halves is a hybrid no
8
+ * real client produces, which is what risk control keys on.
9
+ *
10
+ * Two references, two fingerprints: the hub derives OS/arch and a Node version
11
+ * from its own host, and codebuddy2api pins a fixed Linux/x64/v24.21.0 set.
12
+ * Neither can be right for every deployment, so the values here follow the
13
+ * host this gateway actually runs on — they are a fingerprint of the process,
14
+ * not a claim about a remote machine.
15
+ */
16
+
17
+ /** OpenAI Node SDK 6.25.0 `x-stainless-*` fingerprint; both references agree. */
18
+ const STAINLESS_PACKAGE_VERSION = "6.25.0";
19
+
20
+ function hostOs(): string {
21
+ return process.platform === "win32" ? "Windows" : process.platform === "darwin" ? "MacOS" : "Linux";
22
+ }
23
+
24
+ function hostArch(): string {
25
+ return process.arch === "arm64" || process.arch === "ia32" ? process.arch : "x64";
26
+ }
27
+
28
+ /**
29
+ * The SDK fingerprint headers.
30
+ *
31
+ * `x-stainless-timeout` is in the hub only and `x-stainless-retry-count` is in
32
+ * both; the SDK sends both, so both go out.
33
+ */
34
+ export function buddyStainlessHeaders(): Record<string, string> {
35
+ return {
36
+ "x-stainless-lang": "js",
37
+ "x-stainless-package-version": STAINLESS_PACKAGE_VERSION,
38
+ "x-stainless-os": hostOs(),
39
+ "x-stainless-arch": hostArch(),
40
+ "x-stainless-runtime": "node",
41
+ "x-stainless-runtime-version": process.version,
42
+ "x-stainless-retry-count": "0",
43
+ "x-stainless-timeout": "1200",
44
+ };
45
+ }
46
+
47
+ /**
48
+ * The trace family the CLI carries as a child span.
49
+ *
50
+ * The desktop product omits the parent (`with_parent=False` in the reference);
51
+ * the CLI conversation request is a child span, so it also sends `b3`'s fourth
52
+ * field and `X-B3-ParentSpanId`. `buddyConversationHeaders` already produces
53
+ * `traceparent`, `b3`, and the `X-B3-*` set, so only the parent is added here.
54
+ */
55
+ export function buddyCliTraceHeaders(
56
+ conversation: Readonly<Record<string, string>>,
57
+ ): Record<string, string> {
58
+ const traceId = conversation["x-trace-id"] ?? conversation["x-b3-traceid"];
59
+ const spanId = conversation["x-b3-spanid"];
60
+ if (traceId === undefined) return {};
61
+ const b3 = `${traceId}-${spanId ?? traceId.slice(0, 16)}-1-${traceId.slice(0, 16)}`;
62
+ return { ...(spanId === undefined ? {} : { "X-B3-ParentSpanId": spanId }), b3 };
63
+ }
64
+
65
+ /**
66
+ * The full CLI identity envelope, minus what each caller already supplies.
67
+ *
68
+ * The caller keeps ownership of `User-Agent`, `X-IDE-*`, `X-Domain`, and the
69
+ * conversation family, because those vary by provider and realm: only the
70
+ * headers that are the same for every CLI dispatch are built here.
71
+ */
72
+ export function buddyCliHeaders(args: { readonly userId: string }): Record<string, string> {
73
+ return {
74
+ "X-Agent-Intent": "craft",
75
+ "X-Agent-Type": "main",
76
+ // The reference enum is local-agent / cloud-agent / agent-agent, and its
77
+ // parser returns "other" for anything unknown or absent. A gateway is none
78
+ // of the three, so "other" is the honest value rather than a guess.
79
+ "X-Agent-Runtime": "other",
80
+ "X-User-Id": args.userId,
81
+ // Personal accounts are not an enterprise and the reference sends the flag
82
+ // either way (`true` only for an enterprise id), so the non-enterprise
83
+ // default goes out here. Step 3 replaces this with the account's real
84
+ // enterprise id once discovery carries one.
85
+ "X-Private-Data": "false",
86
+ ...buddyStainlessHeaders(),
87
+ };
88
+ }
@@ -81,14 +81,31 @@ export function buddyConversationHeaders(
81
81
  "x-request-id": messageId,
82
82
  "x-root-request-id": conversationRequestId,
83
83
  "x-trace-id": traceId,
84
+ // W3C trace context alongside the B3 set below: the upstream reads both,
85
+ // and every reference request carries `traceparent`, so sending only the
86
+ // B3 half leaves a header the channel check expects absent.
87
+ traceparent: `00-${b3TraceId}-${messageId.slice(0, 16)}-01`,
84
88
  "x-b3-traceid": b3TraceId,
85
89
  "x-b3-spanid": messageId.slice(0, 16),
86
90
  "x-b3-sampled": "1",
87
91
  };
88
- // Only when the client actually gave one: inventing a conversation id makes
89
- // the upstream build a session the caller never had.
90
- if (ids.conversationId !== undefined && ids.conversationId.trim().length > 0) {
91
- headers["x-conversation-id"] = ids.conversationId.trim();
92
- }
92
+ // Always sent, invented when the caller gave none.
93
+ //
94
+ // This used to be omitted unless the client supplied one. That was wrong:
95
+ // `x-conversation-id` is mandatory, and a chat request without it is refused
96
+ // with `400 · 11-128 "request illegal"` — an error that names neither the
97
+ // header nor the channel, and which reads like a block rather than a missing
98
+ // field. Isolated against the live API by removing one header at a time:
99
+ // only `x-conversation-id`, `x-conversation-request-id` and
100
+ // `x-conversation-message-id` are required; the B3/W3C trace set, the
101
+ // stainless set, the agent block and the IDE block are all optional.
102
+ //
103
+ // Inventing one when the caller has none is still the right default: the id
104
+ // is per-dispatch here, so a caller with no session is not claiming a session
105
+ // it never had — and withholding the header only guarantees a refusal.
106
+ headers["x-conversation-id"] =
107
+ ids.conversationId !== undefined && ids.conversationId.trim().length > 0
108
+ ? ids.conversationId.trim()
109
+ : newMessageId();
93
110
  return headers;
94
111
  }
@@ -37,6 +37,18 @@ export const BUDDY_CN_MODELS_PATH = "/console/enterprises/personal/models" as co
37
37
  */
38
38
  export const BUDDY_INTL_MODELS_PATH = "/v2/enterprises/personal/models" as const;
39
39
 
40
+ /**
41
+ * Remote config path, answered on every realm's chat host.
42
+ *
43
+ * Both reference gateways treat this as the primary model catalog and the
44
+ * personal-models paths above as the narrower fallback: `/v3/config` carries
45
+ * the same `{ code, msg, data: { models, agents } }` envelope plus the
46
+ * per-product rosters the personal path does not always publish. It is tried
47
+ * first, and a failure falls through to the path the caller already passes, so
48
+ * a host that does not answer it behaves exactly as it does today.
49
+ */
50
+ export const BUDDY_CONFIG_PATH = "/v3/config" as const;
51
+
40
52
  /** Envelope code the console directory answers on success. */
41
53
  const BUDDY_OK_CODE = 0;
42
54
 
@@ -92,13 +104,33 @@ export async function fetchBuddyDirectoryModels(args: {
92
104
  readonly fetcher?: typeof fetch | undefined;
93
105
  }): Promise<readonly ModelDefinition[] | null> {
94
106
  const fetcher = args.fetcher ?? globalThis.fetch;
107
+ const primary = await fetchBuddyDirectoryOnce(args, fetcher, BUDDY_CONFIG_PATH);
108
+ if (primary !== null) return primary;
109
+ // Both references rank `/v3/config` above the personal-models path, but this
110
+ // repo has only ever called the latter, so the fallback keeps today's
111
+ // behaviour intact for a host that answers one and not the other.
112
+ return await fetchBuddyDirectoryOnce(args, fetcher, args.modelsPath);
113
+ }
114
+
115
+ /** Reads one directory path; `null` on any transport, status, or shape miss. */
116
+ async function fetchBuddyDirectoryOnce(
117
+ args: {
118
+ readonly siteUrl: string;
119
+ readonly providerId: string;
120
+ readonly credential: string;
121
+ readonly endpoint: string | undefined;
122
+ readonly signal?: AbortSignal | undefined;
123
+ },
124
+ fetcher: typeof fetch,
125
+ path: string,
126
+ ): Promise<readonly ModelDefinition[] | null> {
95
127
  // The directory scores by account: an access token's `sub` is the uid. An
96
128
  // opaque API key carries no identity, and the endpoint still answers for it,
97
129
  // so the header is declared absent rather than guessed.
98
130
  const uid = buddyAccountUid(args.credential);
99
131
  let response: Response;
100
132
  try {
101
- response = await fetcher(`${siteOrigin(args.siteUrl)}${args.modelsPath}`, {
133
+ response = await fetcher(`${siteOrigin(args.siteUrl)}${path}`, {
102
134
  method: "GET",
103
135
  headers: {
104
136
  accept: "application/json",
@@ -0,0 +1,46 @@
1
+ import type { ProviderDispatchContext } from "../../provider-contracts";
2
+
3
+ /**
4
+ * Which upstream client a buddy provider presents itself as.
5
+ *
6
+ * `desktop` is what every buddy provider sends today and stays the default for
7
+ * all four. `cli` is the identity the reference gateways use for CLI traffic: a
8
+ * different header set, a different User-Agent formula, and a different CN
9
+ * upstream.
10
+ *
11
+ * Nothing flips to `cli` implicitly. Being wrong here is what gets an account
12
+ * blocked upstream, so a provider changes identity only when an operator or an
13
+ * account says so.
14
+ */
15
+ export type BuddyIdentity = "desktop" | "cli";
16
+
17
+ /**
18
+ * Resolves the identity one buddy dispatch presents.
19
+ *
20
+ * Precedence is account then provider: an account authorized as a CLI keeps
21
+ * that identity even when the provider default changes, because the upstream
22
+ * recorded it at login. The provider-level flag is
23
+ * `compatibilityProfile.cli_identity`, already declared, validated, and
24
+ * defaulted — it just never reached these adapters before.
25
+ */
26
+ export function resolveBuddyIdentity(
27
+ context: ProviderDispatchContext | undefined,
28
+ profileIdentity?: boolean | undefined,
29
+ ): BuddyIdentity {
30
+ const accountRaw = context?.credential.auth_state?.["cliIdentity"];
31
+ if (accountRaw === true) return "cli";
32
+ if (accountRaw === false) return "desktop";
33
+ return profileIdentity === true ? "cli" : "desktop";
34
+ }
35
+
36
+ /**
37
+ * The upstream's `X-User-Id` for a buddy dispatch.
38
+ *
39
+ * The reference CLI sends the literal `anonymous` when it has no uid rather
40
+ * than omitting the header, so an unauthenticated dispatch still looks like a
41
+ * client rather than like a caller that forgot a header.
42
+ */
43
+ export function buddyUserId(context?: ProviderDispatchContext): string {
44
+ const accountId = context?.credential.account_id;
45
+ return accountId !== undefined && accountId.length > 0 ? accountId : "anonymous";
46
+ }
@@ -280,7 +280,43 @@ export class BuddyOAuthClient extends OAuthDeviceFlow {
280
280
  reason: `${this.#variant.providerId} token response omitted accessToken`,
281
281
  };
282
282
  }
283
- return { status: "complete", result: tokenResult(this.#variant, payload) };
283
+ // The profile nickname is a second, authorized request the reference makes
284
+ // once the token lands — the JWT carries the email but not always a
285
+ // display name. Best-effort by design: a login must not fail because the
286
+ // profile endpoint was unreachable, so every miss falls back to the label
287
+ // the token itself yields.
288
+ const nickname = await this.fetchAccountNickname(deviceAuthId, access);
289
+ if (nickname === undefined) return { status: "complete", result: tokenResult(this.#variant, payload) };
290
+ const result = tokenResult(this.#variant, payload);
291
+ return {
292
+ status: "complete",
293
+ result: { ...result, accountLabel: result.accountLabel ?? nickname },
294
+ };
295
+ }
296
+
297
+ /** Reads the account profile nickname, or `undefined` on any miss. */
298
+ private async fetchAccountNickname(
299
+ deviceAuthId: string,
300
+ accessToken: string,
301
+ ): Promise<string | undefined> {
302
+ try {
303
+ // Both references call `/v2/plugin/login/account` with the token after
304
+ // the poll succeeds; the poll URL is the same versioned plugin root, so
305
+ // the profile path is derived from it rather than declared per variant.
306
+ const url = `${this.#variant.devicePollUrl.replace("/plugin/auth/token", "/plugin/login/account")}?state=${encodeURIComponent(deviceAuthId)}`;
307
+ const response = await this.fetchFn(url, {
308
+ method: "GET",
309
+ headers: {
310
+ ...(await devicePollHeaders(this.#variant)),
311
+ authorization: `Bearer ${accessToken}`,
312
+ },
313
+ });
314
+ if (!response.ok) return undefined;
315
+ const profile = buddyResponseData((await response.json()) as unknown);
316
+ return nonEmptyTrimmedString(profile?.nickname);
317
+ } catch {
318
+ return undefined;
319
+ }
284
320
  }
285
321
 
286
322
  override async refresh(refreshToken: string, signal?: AbortSignal): Promise<OAuthTokenRefreshResult> {
@@ -10,7 +10,7 @@
10
10
  * Deliberately not a request-path store: this is written by a maintenance pass
11
11
  * and read by the console. Nothing here can fail a dispatch.
12
12
  */
13
- import { and, desc, eq, inArray, sql } from "drizzle-orm";
13
+ import { and, desc, eq, gte, inArray, sql } from "drizzle-orm";
14
14
  import type { CartethyiaDatabase } from "../../../persistence/db-handle";
15
15
  import { buddyUsageSnapshots } from "../../../persistence/schema";
16
16
  import type { BuddyUsageRow } from "./buddy-usage-shared";
@@ -23,6 +23,11 @@ export interface BuddyUsageSnapshotRow extends BuddyUsageRow {
23
23
  /** Days of usage retained. The upstream's own window is 30. */
24
24
  const RETENTION_DAYS = 30;
25
25
 
26
+ /** Oldest day a stored bucket can hold, as the `YYYY-MM-DD` key the table uses. */
27
+ function retentionCutoff(now: Date = new Date()): string {
28
+ return new Date(now.getTime() - RETENTION_DAYS * 86_400_000).toISOString().slice(0, 10);
29
+ }
30
+
26
31
  /**
27
32
  * Upserts one account's usage buckets.
28
33
  *
@@ -72,7 +77,31 @@ export async function saveBuddyUsageSnapshots(
72
77
  }
73
78
 
74
79
  /**
75
- * Reads one account's stored usage, newest day first.
80
+ * Most (day, model) buckets one day can hold, used only to bound the read.
81
+ *
82
+ * The renderer keeps {@link DEFAULT_LIMIT_PER_ACCOUNT} entries per account plus
83
+ * every bucket of the newest day, so this is a ceiling on a pathological table
84
+ * rather than a number the page depends on. Set well above any real model count
85
+ * so the cap can only ever trigger on a table that grew without pruning.
86
+ */
87
+ const MODELS_PER_DAY_CEILING = 64;
88
+
89
+ /** Buckets per account the console renders. */
90
+ const DEFAULT_LIMIT_PER_ACCOUNT = 30;
91
+
92
+ /**
93
+ * Reads each account's stored usage, newest day first.
94
+ *
95
+ * Bounded on both axes, because it used to be bounded on neither: with no day
96
+ * filter and no `LIMIT`, an account fetched every bucket it had ever stored
97
+ * (~1500 rows for a 30-day window) to render 30 of them, on the route the
98
+ * dashboard polls.
99
+ *
100
+ * - The day window is semantic, not a truncation: {@link pruneBuddyUsageSnapshots}
101
+ * already drops everything older, so no row it excludes could have been
102
+ * rendered anyway.
103
+ * - The row ceiling is the backstop. It is sized per account, so one large
104
+ * account cannot starve the others in a shared `LIMIT`.
76
105
  *
77
106
  * `numeric` comes back from the driver as a string, so the credit is parsed
78
107
  * rather than trusted; an unparseable row is reported as 0 instead of being
@@ -81,7 +110,7 @@ export async function saveBuddyUsageSnapshots(
81
110
  export async function readBuddyUsageSnapshots(
82
111
  db: CartethyiaDatabase,
83
112
  accountIds: readonly string[],
84
- limitPerAccount = 30,
113
+ limitPerAccount = DEFAULT_LIMIT_PER_ACCOUNT,
85
114
  ): Promise<Map<string, readonly BuddyUsageSnapshotRow[]>> {
86
115
  const result = new Map<string, BuddyUsageSnapshotRow[]>();
87
116
  if (accountIds.length === 0) return result;
@@ -102,8 +131,14 @@ export async function readBuddyUsageSnapshots(
102
131
  syncedAt: buddyUsageSnapshots.syncedAt,
103
132
  })
104
133
  .from(buddyUsageSnapshots)
105
- .where(inArray(buddyUsageSnapshots.accountId, [...accountIds]))
106
- .orderBy(desc(buddyUsageSnapshots.dayKey));
134
+ .where(
135
+ and(
136
+ inArray(buddyUsageSnapshots.accountId, [...accountIds]),
137
+ gte(buddyUsageSnapshots.dayKey, retentionCutoff()),
138
+ ),
139
+ )
140
+ .orderBy(desc(buddyUsageSnapshots.dayKey))
141
+ .limit(accountIds.length * limitPerAccount * MODELS_PER_DAY_CEILING);
107
142
  } catch {
108
143
  return result;
109
144
  }
@@ -106,6 +106,10 @@ export const CODEBUDDY_CN_RAW: readonly BuddyRawEntry[] = [
106
106
  ["glm-5.3-flash", "GLM 5.3 Flash", true, true, 1_000_000, 131_072],
107
107
  ["glm-5v-turbo", "GLM 5v Turbo", true, true, 200_000, 131_072],
108
108
  ["minimax-m3", "MiniMax M3", true, true, 512_000, 512_000],
109
+ // 1M context, reasoning always on (upstream states both); vision inherited
110
+ // from the M3 row, which is the same family.
111
+ ["minimax-m3.1-flash", "MiniMax M3.1 Flash", true, true, 1_000_000, 512_000],
112
+ ["mimo-v2.6-pro", "MiMo V2.6 Pro", true, true, 1_000_000, 512_000],
109
113
  ["kimi-k2.8-preview", "Kimi K2.8 Preview", true, true, 1_048_576, 65_536],
110
114
  ["kimi-k2.7", "Kimi K2.7", true, true, 256_000, 65_536],
111
115
  ["kimi-k2.6", "Kimi K2.6", true, true, 256_000, 262_144],