cartethyia 2.7.4 → 2.7.5

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 (553) hide show
  1. package/.env.example +2 -0
  2. package/README.md +17 -5
  3. package/dist/dashboard/assets/{BannedPage-ChG5svFb.js → BannedPage-Cj15GsMp.js} +1 -1
  4. package/dist/dashboard/assets/BannedPage-Cj15GsMp.js.gz +0 -0
  5. package/dist/dashboard/assets/ClaimGiftsButton-D83pBJ6J.js +1 -0
  6. package/dist/dashboard/assets/ClaimGiftsButton-D83pBJ6J.js.gz +0 -0
  7. package/dist/dashboard/assets/{CliToolDetailPage-BIcWQun7.js → CliToolDetailPage-DZKmVuJN.js} +1 -1
  8. package/dist/dashboard/assets/CliToolDetailPage-DZKmVuJN.js.gz +0 -0
  9. package/dist/dashboard/assets/{CliToolsPage-Bf2VelDe.js → CliToolsPage-BIccZmeL.js} +1 -1
  10. package/dist/dashboard/assets/CliToolsPage-BIccZmeL.js.gz +0 -0
  11. package/dist/dashboard/assets/CombosPage-DEEKkYD-.js +1 -0
  12. package/dist/dashboard/assets/CombosPage-DEEKkYD-.js.gz +0 -0
  13. package/dist/dashboard/assets/ConsoleLogPage-D5rol2j2.js +1 -0
  14. package/dist/dashboard/assets/ConsoleLogPage-D5rol2j2.js.gz +0 -0
  15. package/dist/dashboard/assets/{CustomizationPage-ggeMSjte.js → CustomizationPage-BKj_r0rp.js} +1 -1
  16. package/dist/dashboard/assets/CustomizationPage-BKj_r0rp.js.gz +0 -0
  17. package/dist/dashboard/assets/InjectSystemPromptPanel-B5nZvmf0.js +656 -0
  18. package/dist/dashboard/assets/InjectSystemPromptPanel-B5nZvmf0.js.gz +0 -0
  19. package/dist/dashboard/assets/{LoginPage-U6AKaJcF.js → LoginPage-DkXb6dx-.js} +1 -1
  20. package/dist/dashboard/assets/LoginPage-DkXb6dx-.js.gz +0 -0
  21. package/dist/dashboard/assets/{ModelPicker-BMS2t8BA.js → ModelPicker-DnQPSIA8.js} +1 -1
  22. package/dist/dashboard/assets/ModelPicker-DnQPSIA8.js.gz +0 -0
  23. package/dist/dashboard/assets/OverviewPage-Ci2wQKUz.js +1 -0
  24. package/dist/dashboard/assets/OverviewPage-Ci2wQKUz.js.gz +0 -0
  25. package/dist/dashboard/assets/ProviderDetailPage-odlgYrOg.js +5 -0
  26. package/dist/dashboard/assets/ProviderDetailPage-odlgYrOg.js.gz +0 -0
  27. package/dist/dashboard/assets/ProviderIcon-EfeloBn4.js +1 -0
  28. package/dist/dashboard/assets/ProviderIcon-EfeloBn4.js.gz +0 -0
  29. package/dist/dashboard/assets/ProvidersPage-CqFBQEB8.js +1 -0
  30. package/dist/dashboard/assets/ProvidersPage-CqFBQEB8.js.gz +0 -0
  31. package/dist/dashboard/assets/{ProxyPage-lsrx7NYg.js → ProxyPage-5SnUSy3j.js} +1 -1
  32. package/dist/dashboard/assets/ProxyPage-5SnUSy3j.js.gz +0 -0
  33. package/dist/dashboard/assets/QuotaPage-CmnMDzhP.js +1 -0
  34. package/dist/dashboard/assets/QuotaPage-CmnMDzhP.js.gz +0 -0
  35. package/dist/dashboard/assets/SettingsPage-BJtRZ4qD.js +1 -0
  36. package/dist/dashboard/assets/SettingsPage-BJtRZ4qD.js.gz +0 -0
  37. package/dist/dashboard/assets/{SetupPage-f0DE1h0-.js → SetupPage-WTa0sktC.js} +1 -1
  38. package/dist/dashboard/assets/SetupPage-WTa0sktC.js.gz +0 -0
  39. package/dist/dashboard/assets/{StudioPage-Cq5tW7hW.js → StudioPage-BfAb5-o_.js} +3 -3
  40. package/dist/dashboard/assets/StudioPage-BfAb5-o_.js.gz +0 -0
  41. package/dist/dashboard/assets/UsagePage-BNzeZ2Vk.js +56 -0
  42. package/dist/dashboard/assets/UsagePage-BNzeZ2Vk.js.gz +0 -0
  43. package/dist/dashboard/assets/api-keys-l29JROPv.js +1 -0
  44. package/dist/dashboard/assets/api-keys-l29JROPv.js.gz +0 -0
  45. package/dist/dashboard/assets/app--PJWq8_C.css +2 -0
  46. package/dist/dashboard/assets/app--PJWq8_C.css.gz +0 -0
  47. package/dist/dashboard/assets/assert-C0K-c4eo.js +1 -0
  48. package/dist/dashboard/assets/assert-C0K-c4eo.js.gz +0 -0
  49. package/dist/dashboard/assets/{badge-D9CS6dZk.js → badge-DfbhQU4k.js} +1 -1
  50. package/dist/dashboard/assets/badge-DfbhQU4k.js.gz +0 -0
  51. package/dist/dashboard/assets/{banner-DympBNq4.js → banner-BllT4LcF.js} +1 -1
  52. package/dist/dashboard/assets/banner-BllT4LcF.js.gz +0 -0
  53. package/dist/dashboard/assets/brand/login_register.jpg +0 -0
  54. package/dist/dashboard/assets/brand/login_register_initial.jpg +0 -0
  55. package/dist/dashboard/assets/brand/og_bansos.jpg +0 -0
  56. package/dist/dashboard/assets/brand/og_image.jpg +0 -0
  57. package/dist/dashboard/assets/{card-ibRWU_uT.js → card-CtkX66Wd.js} +1 -1
  58. package/dist/dashboard/assets/card-CtkX66Wd.js.gz +0 -0
  59. package/dist/dashboard/assets/chevron-left-Bf6CaTSJ.js +1 -0
  60. package/dist/dashboard/assets/chevron-left-Bf6CaTSJ.js.gz +0 -0
  61. package/dist/dashboard/assets/cli-tools-CQXVMn4P.js +1 -0
  62. package/dist/dashboard/assets/cli-tools-CQXVMn4P.js.gz +0 -0
  63. package/dist/dashboard/assets/{clipboard-button-DJm-9pz7.js → clipboard-button-BQqBQd2n.js} +1 -1
  64. package/dist/dashboard/assets/clipboard-button-BQqBQd2n.js.gz +0 -0
  65. package/dist/dashboard/assets/{dialog-8fiE6tec.js → dialog-BEK7KP0H.js} +1 -1
  66. package/dist/dashboard/assets/dialog-BEK7KP0H.js.gz +0 -0
  67. package/dist/dashboard/assets/{drawer-CqGYeOrd.js → drawer-BrTEZ8zu.js} +1 -1
  68. package/dist/dashboard/assets/drawer-BrTEZ8zu.js.gz +0 -0
  69. package/dist/dashboard/assets/entry-CHWxbS56.js +1 -0
  70. package/dist/dashboard/assets/entry-CHWxbS56.js.gz +0 -0
  71. package/dist/dashboard/assets/{entry-BSAsYsmV.js → entry-EBHJ3DrE.js} +1 -1
  72. package/dist/dashboard/assets/entry-EBHJ3DrE.js.gz +0 -0
  73. package/dist/dashboard/assets/entry-LCS85hiq.js +4 -0
  74. package/dist/dashboard/assets/entry-LCS85hiq.js.gz +0 -0
  75. package/dist/dashboard/assets/{formatters-D-MTJxMr.js → formatters-BAACA727.js} +1 -1
  76. package/dist/dashboard/assets/formatters-BAACA727.js.gz +0 -0
  77. package/dist/dashboard/assets/{github-badge-CPK47xQx.js → github-badge-YSCE4UC2.js} +1 -1
  78. package/dist/dashboard/assets/github-badge-YSCE4UC2.js.gz +0 -0
  79. package/dist/dashboard/assets/{index-DBNPOfhV.js → index-FQlj0HUT.js} +2 -2
  80. package/dist/dashboard/assets/index-FQlj0HUT.js.gz +0 -0
  81. package/dist/dashboard/assets/{inline-BT0zntEr.js → inline-C4gU5iOL.js} +1 -1
  82. package/dist/dashboard/assets/inline-C4gU5iOL.js.gz +0 -0
  83. package/dist/dashboard/assets/{input-Da2ZViWg.js → input-BHHX_Tmm.js} +1 -1
  84. package/dist/dashboard/assets/input-BHHX_Tmm.js.gz +0 -0
  85. package/dist/dashboard/assets/{layout-BXNeLw6M.js → layout-CajrYUnD.js} +1 -1
  86. package/dist/dashboard/assets/layout-CajrYUnD.js.gz +0 -0
  87. package/dist/dashboard/assets/{list-row-BycOrrIV.js → list-row-Bx2I7XUe.js} +1 -1
  88. package/dist/dashboard/assets/list-row-Bx2I7XUe.js.gz +0 -0
  89. package/dist/dashboard/assets/live-DNNqRbj2.js +1 -0
  90. package/dist/dashboard/assets/live-DNNqRbj2.js.gz +0 -0
  91. package/dist/dashboard/assets/network-C5UBACrJ.js +1 -0
  92. package/dist/dashboard/assets/network-C5UBACrJ.js.gz +0 -0
  93. package/dist/dashboard/assets/numbers-CUWAZK4J.js +2 -0
  94. package/dist/dashboard/assets/numbers-CUWAZK4J.js.gz +0 -0
  95. package/dist/dashboard/assets/pagination-Xsi9Qx04.js +1 -0
  96. package/dist/dashboard/assets/pagination-Xsi9Qx04.js.gz +0 -0
  97. package/dist/dashboard/assets/quota-DvQbD_sr.js +1 -0
  98. package/dist/dashboard/assets/quota-DvQbD_sr.js.gz +0 -0
  99. package/dist/dashboard/assets/routing-in0f_IfK.js +1 -0
  100. package/dist/dashboard/assets/routing-in0f_IfK.js.gz +0 -0
  101. package/dist/dashboard/assets/{select-BIYIBkMU.js → select-BLULNqTp.js} +1 -1
  102. package/dist/dashboard/assets/select-BLULNqTp.js.gz +0 -0
  103. package/dist/dashboard/assets/settings-CcGczj6I.js +1 -0
  104. package/dist/dashboard/assets/settings-CcGczj6I.js.gz +0 -0
  105. package/dist/dashboard/assets/shield-BMQNxORh.js +1 -0
  106. package/dist/dashboard/assets/shield-BMQNxORh.js.gz +0 -0
  107. package/dist/dashboard/assets/{stack-DfgnFDWY.js → stack-D2lIR0tg.js} +1 -1
  108. package/dist/dashboard/assets/stack-D2lIR0tg.js.gz +0 -0
  109. package/dist/dashboard/assets/{state-CkfdYWel.js → state-DTARjfiW.js} +1 -1
  110. package/dist/dashboard/assets/state-DTARjfiW.js.gz +0 -0
  111. package/dist/dashboard/assets/{switch-SYm237HG.js → switch-c0oPSnqW.js} +1 -1
  112. package/dist/dashboard/assets/switch-c0oPSnqW.js.gz +0 -0
  113. package/dist/dashboard/assets/{useMutation-BQqCRNIi.js → useMutation-m8WGNPrz.js} +1 -1
  114. package/dist/dashboard/assets/useMutation-m8WGNPrz.js.gz +0 -0
  115. package/dist/dashboard/console-backgrounds.jpg +0 -0
  116. package/dist/dashboard/console-backgrounds_initial.jpg +0 -0
  117. package/dist/dashboard/index.html +2 -2
  118. package/dist/dashboard/index.html.gz +0 -0
  119. package/dist/dashboard/precompressed-assets.json +56 -55
  120. package/migrations/full-postgres/0036_system_prompt_presets_refs.sql +10 -0
  121. package/migrations/full-postgres/0037_share_concurrency_scope.sql +3 -0
  122. package/migrations/full-postgres/0038_add_accounts_quota_snapshot.sql +7 -0
  123. package/migrations/full-postgres/0039_add_telemetry_provider_model_indexes.sql +13 -0
  124. package/migrations/full-postgres/0040_add_models_dialect.sql +3 -0
  125. package/migrations/full-postgres/0041_add_daily_checkin_ledger_account_fk.sql +7 -0
  126. package/migrations/lite-sqlite3/0036_system_prompt_presets_refs.sql +14 -0
  127. package/migrations/lite-sqlite3/0037_share_concurrency_scope.sql +3 -0
  128. package/migrations/lite-sqlite3/0038_add_accounts_quota_snapshot.sql +4 -0
  129. package/migrations/lite-sqlite3/0039_add_telemetry_provider_model_indexes.sql +6 -0
  130. package/migrations/lite-sqlite3/0040_add_models_dialect.sql +3 -0
  131. package/migrations/lite-sqlite3/0041_add_daily_checkin_ledger_account_fk.sql +26 -0
  132. package/package.json +4 -6
  133. package/scripts/cli/update.ts +15 -41
  134. package/src/boot/app.ts +58 -2
  135. package/src/boot/dependencies.ts +786 -732
  136. package/src/boot/lifecycle.ts +11 -12
  137. package/src/config/cooldowns.ts +34 -55
  138. package/src/config/network.ts +1 -1
  139. package/src/config/package-paths.ts +1 -1
  140. package/src/config/providers.ts +13 -14
  141. package/src/config/resources.ts +0 -5
  142. package/src/config/runtime.ts +2 -5
  143. package/src/config/spec.ts +26 -6
  144. package/src/console/api-keys/ban-constants.ts +38 -0
  145. package/src/console/api-keys/contracts.ts +71 -62
  146. package/src/console/api-keys/routes.ts +48 -12
  147. package/src/console/auth/access.ts +4 -4
  148. package/src/console/auth/account-reset.ts +1 -1
  149. package/src/console/auth/audit-recorder.ts +1 -1
  150. package/src/console/auth/lockout.ts +7 -3
  151. package/src/console/auth/row-coercion.ts +57 -12
  152. package/src/console/auth/session-resolver.ts +1 -1
  153. package/src/console/auth/session-service.ts +8 -2
  154. package/src/console/auth/session.ts +37 -31
  155. package/src/console/auth/setup-service.ts +4 -3
  156. package/src/console/auth/user-store.ts +1 -1
  157. package/src/console/backup/9router-map.ts +0 -3
  158. package/src/console/backup/contracts.ts +9 -9
  159. package/src/console/backup/export-endpoint.ts +5 -6
  160. package/src/console/backup/nine-router.ts +1 -7
  161. package/src/console/backup/routes.ts +29 -33
  162. package/src/console/backup/service.ts +468 -468
  163. package/src/console/backup/store.ts +47 -29
  164. package/src/console/backup/validate.ts +11 -1
  165. package/src/console/cli-tools/contracts.ts +7 -7
  166. package/src/console/cli-tools/injectors/cline.ts +110 -110
  167. package/src/console/cli-tools/injectors/codex.ts +181 -181
  168. package/src/console/cli-tools/injectors/copilot.ts +108 -108
  169. package/src/console/cli-tools/injectors/driver.ts +247 -247
  170. package/src/console/cli-tools/injectors/jcode.ts +115 -115
  171. package/src/console/cli-tools/injectors/openclaw.ts +161 -161
  172. package/src/console/cli-tools/injectors/opencode.ts +153 -153
  173. package/src/console/cli-tools/routes.ts +1 -1
  174. package/src/console/cli-tools/service.ts +1 -1
  175. package/src/console/cli-tools/store.ts +2 -2
  176. package/src/console/console-router.ts +32 -0
  177. package/src/console/dashboard-assets.ts +9 -9
  178. package/src/console/domain-registration.ts +16 -2
  179. package/src/console/inferhub/client.ts +0 -7
  180. package/src/console/inferhub/key-store.ts +1 -1
  181. package/src/console/inferhub/normalize.ts +2 -2
  182. package/src/console/inferhub/routes.ts +1 -1
  183. package/src/console/inferhub/types.ts +1 -1
  184. package/src/console/observability/contracts.ts +1 -1
  185. package/src/console/observability/live.ts +31 -18
  186. package/src/console/observability/logs.ts +1 -1
  187. package/src/console/observability/sse.ts +62 -8
  188. package/src/console/observability/store.ts +20 -4
  189. package/src/console/providers/catalog/catalog-projections.ts +1 -1
  190. package/src/console/providers/catalog/contracts.ts +39 -0
  191. package/src/console/providers/catalog/model-operations.ts +1 -1
  192. package/src/console/providers/catalog/provider-operations.ts +95 -17
  193. package/src/console/providers/catalog/public-model-store.ts +0 -0
  194. package/src/console/providers/catalog/routes.ts +27 -3
  195. package/src/console/providers/catalog/store.ts +147 -8
  196. package/src/console/providers/detail/routes.ts +1 -1
  197. package/src/console/providers/oauth/callback-listener.ts +100 -18
  198. package/src/console/providers/oauth/routes.ts +92 -13
  199. package/src/console/quota/account-quota-global.ts +41 -3
  200. package/src/console/quota/account-quota-shared.ts +10 -3
  201. package/src/console/quota/account-quota-tenant.ts +259 -66
  202. package/src/console/quota/daily-growth-job.ts +308 -0
  203. package/src/console/quota/native-identity.ts +2 -2
  204. package/src/console/quota/quota-view.ts +1 -1
  205. package/src/console/remote-support/routes.ts +1 -1
  206. package/src/console/remote-support/service.ts +2 -2
  207. package/src/console/routing/model/contracts.ts +1 -1
  208. package/src/console/routing/pools/contracts.ts +2 -2
  209. package/src/console/routing/pools/import-parse.ts +3 -3
  210. package/src/console/routing/pools/relay-deploy.ts +3 -3
  211. package/src/console/settings/contracts.ts +29 -2
  212. package/src/console/settings/store.ts +20 -0
  213. package/src/console/share/share-router.ts +100 -32
  214. package/src/console/share/share-stats.ts +4 -4
  215. package/src/console/share/share-usage.ts +1 -1
  216. package/src/console/shared/errors.ts +2 -2
  217. package/src/console/shared/object.ts +18 -0
  218. package/src/console/shared/read-through-cache.ts +4 -4
  219. package/src/console/shared/transaction.ts +14 -0
  220. package/src/console/system-prompt/contracts.ts +89 -12
  221. package/src/console/system-prompt/routes.ts +98 -45
  222. package/src/console/updates/apply-update.ts +244 -0
  223. package/src/console/updates/routes.ts +252 -97
  224. package/src/main.ts +11 -5
  225. package/src/network/http2-bidi.ts +1 -1
  226. package/src/network/ip-utils.ts +298 -0
  227. package/src/network/outbound-fetch.ts +1 -1
  228. package/src/network/pool/agent.ts +7 -6
  229. package/src/network/pool/byte-accounting.ts +1 -1
  230. package/src/network/pool/resolver.ts +16 -12
  231. package/src/network/pool/selector.ts +150 -136
  232. package/src/network/pool-health-machine.ts +1 -1
  233. package/src/network/rate-limit-verdict.ts +1 -1
  234. package/src/network/retry.ts +1 -1
  235. package/src/network/ssrf.ts +20 -71
  236. package/src/observability/event-log.ts +2 -2
  237. package/src/observability/event-query.ts +59 -291
  238. package/src/observability/log-sanitize.ts +1 -1
  239. package/src/observability/metrics.ts +4 -4
  240. package/src/observability/payload-capture.ts +6 -5
  241. package/src/observability/payload-store.ts +1 -1
  242. package/src/observability/runtime-metrics.ts +1 -1
  243. package/src/observability/telemetry-buffer.ts +12 -3
  244. package/src/observability/telemetry-reconciliation.ts +3 -3
  245. package/src/observability/token-speed.ts +1 -1
  246. package/src/persistence/connection-url.ts +1 -1
  247. package/src/persistence/db-handle.ts +1 -1
  248. package/src/persistence/db-pg.ts +2 -2
  249. package/src/persistence/db-sqlite.ts +3 -3
  250. package/src/persistence/dialect.ts +5 -1
  251. package/src/persistence/readiness.ts +1 -1
  252. package/src/persistence/redis.ts +1 -1
  253. package/src/persistence/schema.ts +113 -40
  254. package/src/persistence/shared-cache.ts +2 -2
  255. package/src/persistence/sort-order.ts +1 -1
  256. package/src/persistence/stores/api-key-store.ts +10 -1
  257. package/src/persistence/stores/share-store.ts +20 -8
  258. package/src/persistence/system-prompt-store.ts +63 -18
  259. package/src/persistence/tenant-preferences.ts +1 -1
  260. package/src/persistence/ttl-cache.ts +3 -3
  261. package/src/persistence/usage-flush.ts +7 -2
  262. package/src/protocol/canonical-model.ts +1 -1
  263. package/src/protocol/codecs.ts +6 -10
  264. package/src/protocol/dsml.ts +3 -49
  265. package/src/protocol/image-clamp.ts +2 -2
  266. package/src/protocol/reasoning-text.ts +1 -1
  267. package/src/protocol/reasoning.ts +7 -52
  268. package/src/protocol/registry.ts +50 -15
  269. package/src/protocol/request/chat.ts +46 -19
  270. package/src/protocol/request/codex.ts +1 -1
  271. package/src/protocol/request/messages.ts +46 -14
  272. package/src/protocol/request/responses.ts +19 -10
  273. package/src/protocol/response/chat.ts +25 -10
  274. package/src/protocol/response/codex.ts +1 -1
  275. package/src/protocol/response/messages.ts +1 -1
  276. package/src/protocol/response/omp-tool-call-markup.ts +1 -1
  277. package/src/protocol/response/responses.ts +1 -1
  278. package/src/protocol/sse-decoder.ts +50 -1
  279. package/src/protocol/stop-reason.ts +2 -2
  280. package/src/protocol/think-split.ts +347 -0
  281. package/src/protocol/translation/attachment-integrity.ts +3 -3
  282. package/src/protocol/translation/cache-controls.ts +8 -20
  283. package/src/protocol/translation/capabilities.ts +6 -6
  284. package/src/protocol/translation/dialect/deepseek.ts +10 -0
  285. package/src/protocol/translation/dialect/inband.ts +39 -0
  286. package/src/protocol/translation/dialect/model-thinking.ts +89 -0
  287. package/src/protocol/translation/dialect/qwen.ts +114 -0
  288. package/src/protocol/translation/dialect/resolver.ts +83 -0
  289. package/src/protocol/translation/dialect/rules.ts +93 -0
  290. package/src/protocol/translation/dialect/thinking-ladder.ts +245 -0
  291. package/src/protocol/translation/dialect/types.ts +58 -0
  292. package/src/protocol/translation/quirks.ts +18 -4
  293. package/src/protocol/translation/thinking.ts +12 -252
  294. package/src/protocol/translation/tool-id.ts +3 -3
  295. package/src/protocol/translation/tool-repair.ts +1 -1
  296. package/src/protocol/upstream/post-json.ts +6 -1
  297. package/src/providers/adapters/compatible-adapter.ts +42 -3
  298. package/src/providers/authentication/jwt-validator.ts +0 -5
  299. package/src/providers/authentication/oauth-client.ts +254 -253
  300. package/src/providers/authentication/oauth-flow-store.ts +42 -7
  301. package/src/providers/bundled-discovery-providers.ts +6 -30
  302. package/src/providers/configured-openai-providers.ts +82 -97
  303. package/src/providers/default-registry.ts +23 -35
  304. package/src/providers/discovery/catalog/catalog-format.ts +13 -2
  305. package/src/providers/discovery/discovery-types.ts +0 -1
  306. package/src/providers/discovery/openai-model-discovery.ts +2 -2
  307. package/src/providers/discovery/probe-phases.ts +48 -13
  308. package/src/providers/discovery/probing-service.ts +6 -8
  309. package/src/providers/integrations/agentrouter/agentrouter.ts +12 -2
  310. package/src/providers/integrations/antigravity/antigravity-oauth.ts +0 -1
  311. package/src/providers/integrations/antigravity/antigravity-protocol.ts +0 -14
  312. package/src/providers/integrations/antigravity/antigravity.ts +2 -2
  313. package/src/providers/integrations/buddy/buddy-catalog-shared.ts +1 -1
  314. package/src/providers/integrations/buddy/buddy-chat-shared.ts +20 -4
  315. package/src/providers/integrations/buddy/buddy-cli-headers.ts +19 -52
  316. package/src/providers/integrations/buddy/buddy-identity.ts +86 -0
  317. package/src/providers/integrations/buddy/buddy-oauth-shared.ts +1 -1
  318. package/src/providers/integrations/buddy/codebuddy/cn-provider.ts +1 -2
  319. package/src/providers/integrations/buddy/codebuddy/provider.ts +4 -15
  320. package/src/providers/integrations/buddy/codebuddy/shared.ts +30 -9
  321. package/src/providers/integrations/buddy/workbuddy/provider.ts +5 -9
  322. package/src/providers/integrations/buddy/workbuddy/shared.ts +21 -3
  323. package/src/providers/integrations/claude/claude-cch.ts +148 -148
  324. package/src/providers/integrations/claude/fallback-credit.ts +3 -7
  325. package/src/providers/integrations/cline/cline.ts +1 -1
  326. package/src/providers/integrations/codex/codex-device-code.ts +101 -101
  327. package/src/providers/integrations/codex/codex-oauth.ts +0 -1
  328. package/src/providers/integrations/commandcode/commandcode.ts +75 -115
  329. package/src/providers/integrations/commandcode/quota.ts +131 -51
  330. package/src/providers/integrations/cursor/cursor-http2.ts +0 -3
  331. package/src/providers/integrations/cursor/cursor-quota.ts +2 -6
  332. package/src/providers/integrations/cursor/cursor-request.ts +0 -87
  333. package/src/providers/integrations/cursor/cursor-shared.ts +0 -4
  334. package/src/providers/integrations/cursor/cursor.ts +38 -5
  335. package/src/providers/integrations/cursor/protocol/framing.ts +0 -56
  336. package/src/providers/integrations/cursor/protocol/messages.ts +0 -96
  337. package/src/providers/integrations/factory/factory.ts +9 -9
  338. package/src/providers/integrations/github/github-discovery.ts +55 -10
  339. package/src/providers/integrations/github/github-oauth.ts +6 -5
  340. package/src/providers/integrations/github/github.ts +41 -14
  341. package/src/providers/integrations/grok/grok-oauth.ts +261 -261
  342. package/src/providers/integrations/grok/grok-quota-frame.ts +1 -1
  343. package/src/providers/integrations/grok/grok-quota.ts +16 -7
  344. package/src/providers/integrations/grok/grok-ratelimit-store.ts +0 -5
  345. package/src/providers/integrations/grok/grok-ratelimit.ts +3 -0
  346. package/src/providers/integrations/grok/grok.ts +86 -36
  347. package/src/providers/integrations/kilo/kilo-oauth.ts +1 -1
  348. package/src/providers/integrations/kiro/kiro-cache.ts +31 -0
  349. package/src/providers/integrations/kiro/kiro-catalog.ts +31 -5
  350. package/src/providers/integrations/kiro/kiro-discovery.ts +47 -43
  351. package/src/providers/integrations/kiro/kiro-machine-id.ts +6 -3
  352. package/src/providers/integrations/kiro/kiro-oauth.ts +523 -255
  353. package/src/providers/integrations/kiro/kiro-quota.ts +35 -47
  354. package/src/providers/integrations/kiro/kiro-request.ts +8 -11
  355. package/src/providers/integrations/kiro/kiro-stream.ts +13 -0
  356. package/src/providers/integrations/kiro/kiro.ts +81 -68
  357. package/src/providers/integrations/muse/muse-oauth.ts +0 -1
  358. package/src/providers/integrations/muse/muse-quota.ts +0 -4
  359. package/src/providers/integrations/opencode/fingerprint.ts +7 -5
  360. package/src/providers/integrations/opencode/provider.ts +17 -24
  361. package/src/providers/integrations/qoder/qoder-catalog.ts +85 -8
  362. package/src/providers/integrations/qoder/qoder-oauth.ts +165 -25
  363. package/src/providers/integrations/qoder/qoder-quota.ts +48 -6
  364. package/src/providers/integrations/qoder/qoder-realm.ts +10 -31
  365. package/src/providers/integrations/qoder/qoder-sse.ts +159 -0
  366. package/src/providers/integrations/qoder/qoder-umid.ts +21 -25
  367. package/src/providers/integrations/qoder/qoder-virtual.ts +12 -0
  368. package/src/providers/integrations/qoder/qoder.ts +354 -62
  369. package/src/providers/integrations/shared/connect.ts +0 -54
  370. package/src/providers/integrations/shared/hash.ts +15 -0
  371. package/src/providers/integrations/shared/session-cache.ts +136 -0
  372. package/src/providers/integrations/trae/trae-cache.ts +7 -87
  373. package/src/providers/integrations/trae/trae-catalog.ts +1 -6
  374. package/src/providers/integrations/trae/trae-protocol.ts +17 -8
  375. package/src/providers/integrations/trae/trae-realm.ts +0 -7
  376. package/src/providers/integrations/trae/trae.ts +53 -10
  377. package/src/providers/integrations/xai/xai-oauth.ts +5 -5
  378. package/src/providers/integrations/zcode-start-plan/gifts.ts +265 -0
  379. package/src/providers/integrations/zcode-start-plan/oauth.ts +154 -0
  380. package/src/providers/integrations/zcode-start-plan/provider.ts +131 -0
  381. package/src/providers/integrations/zcode-start-plan/quota.ts +152 -0
  382. package/src/providers/integrations/zcode-start-plan/zcode_prompt.json +10 -0
  383. package/src/providers/operations/account-error-classification.ts +131 -116
  384. package/src/providers/operations/account-health-service.ts +31 -39
  385. package/src/providers/operations/account-reset-service.ts +58 -1
  386. package/src/providers/operations/cli-platform.ts +31 -5
  387. package/src/providers/operations/client-version-resolver.ts +4 -6
  388. package/src/providers/operations/client-versions.ts +10 -7
  389. package/src/providers/operations/provider-catalog-seeder.ts +2 -0
  390. package/src/providers/operations/provider-catalog-service.ts +18 -2
  391. package/src/providers/operations/provider-credential-service.ts +19 -2
  392. package/src/providers/provider-contracts.ts +18 -7
  393. package/src/providers/provider-metadata.ts +17 -2
  394. package/src/providers/provider-model-definition.ts +22 -1
  395. package/src/providers/provider-probe.ts +4 -2
  396. package/src/providers/provider-usage.ts +81 -16
  397. package/src/providers/quota/cache.ts +29 -0
  398. package/src/providers/quota/quota-contracts.ts +4 -0
  399. package/src/providers/quota/refresh.ts +27 -4
  400. package/src/providers/quota/snapshot.ts +94 -0
  401. package/src/providers/search/search-provider.ts +1 -3
  402. package/src/providers/search/search-providers.ts +2 -1
  403. package/src/security/abuse.ts +4 -4
  404. package/src/security/admission/service.ts +2 -2
  405. package/src/security/admission/ttl.ts +61 -61
  406. package/src/security/api-key-auth.ts +16 -4
  407. package/src/security/client-router-fingerprint.ts +4 -4
  408. package/src/security/credential-codec.ts +3 -3
  409. package/src/security/csrf.ts +2 -2
  410. package/src/security/ip-boundary.ts +16 -189
  411. package/src/security/model-access-rule.ts +1 -1
  412. package/src/security/outbound-headers.ts +18 -0
  413. package/src/shared/invoke-guard.ts +47 -0
  414. package/src/transport/contracts.ts +13 -4
  415. package/src/transport/dispatch/attempt-finalize.ts +1 -1
  416. package/src/transport/dispatch/capture-policy.ts +34 -37
  417. package/src/transport/dispatch/empty-stream.ts +23 -20
  418. package/src/transport/dispatch/generation.ts +25 -45
  419. package/src/transport/dispatch/native-shared.ts +127 -0
  420. package/src/transport/dispatch/proxy-request.ts +3 -1
  421. package/src/transport/dispatch/responses-compact.ts +25 -46
  422. package/src/transport/dispatch/streaming/encoder.ts +18 -72
  423. package/src/transport/dispatch/streaming/finalize.ts +2 -2
  424. package/src/transport/dispatch/streaming/stream.ts +7 -3
  425. package/src/transport/dispatch/streaming/thinking-loop.ts +1 -1
  426. package/src/transport/dispatch/systemone.ts +20 -48
  427. package/src/transport/dispatch/token-count.ts +41 -38
  428. package/src/transport/dispatch/websearch.ts +19 -45
  429. package/src/transport/drain-endpoint.ts +1 -1
  430. package/src/transport/failure-policy.ts +6 -0
  431. package/src/transport/guards/auth-guard.ts +0 -6
  432. package/src/transport/guards/body-policy.ts +3 -3
  433. package/src/transport/guards/headroom-guard.ts +1 -1
  434. package/src/transport/guards/pipeline.ts +1 -1
  435. package/src/transport/guards/request-context.ts +12 -17
  436. package/src/transport/request/preparer.ts +286 -7
  437. package/src/transport/request/rtk/compress-request.ts +3 -3
  438. package/src/transport/request/rtk/rtk-constants.ts +2 -2
  439. package/src/transport/request/rtk/rtk-filters.ts +8 -8
  440. package/src/transport/request/state.ts +1 -13
  441. package/src/transport/request/system-prompt-injection.ts +74 -11
  442. package/src/transport/request/tenant-preferences.ts +22 -0
  443. package/src/transport/routing/candidate-index.ts +2 -2
  444. package/src/transport/routing/round-robin.ts +1 -1
  445. package/src/transport/routing/route-catalog.ts +17 -18
  446. package/src/transport/routing/route-model.ts +126 -33
  447. package/src/transport/routing/router.ts +152 -37
  448. package/src/transport/shutdown-notice.ts +2 -2
  449. package/src/transport/surface/adapters.ts +3 -3
  450. package/src/transport/surface/chat/encode.ts +7 -7
  451. package/src/transport/surface/chat/parse.ts +7 -7
  452. package/src/transport/surface/completion.ts +2 -2
  453. package/src/transport/surface/content-parts.ts +1 -1
  454. package/src/transport/surface/dialects.ts +2 -2
  455. package/src/transport/surface/messages/parse.ts +3 -3
  456. package/src/transport/surface/responses/adapter.ts +1 -1
  457. package/src/transport/surface/responses/errors.ts +0 -9
  458. package/src/transport/vision-handoff-cache.ts +58 -0
  459. package/src/transport/vision-handoff.ts +376 -0
  460. package/src/workers/account-identity-sweep.ts +2 -2
  461. package/src/workers/buddy-growth-tasks.ts +392 -346
  462. package/src/workers/daily-checkin.ts +102 -49
  463. package/src/workers/maintenance-lease.ts +2 -35
  464. package/src/workers/oauth-refresh-sweep.ts +8 -1
  465. package/src/workers/quota-refresh-sweep.ts +120 -82
  466. package/src/workers/scheduler.ts +67 -4
  467. package/src/workers/sweep-runner.ts +24 -5
  468. package/src/workers/zcode-gift-claim.ts +108 -0
  469. package/dist/dashboard/assets/AccountCooldown-Bjo92Qat.js +0 -1
  470. package/dist/dashboard/assets/AccountCooldown-Bjo92Qat.js.gz +0 -0
  471. package/dist/dashboard/assets/BannedPage-ChG5svFb.js.gz +0 -0
  472. package/dist/dashboard/assets/CliToolDetailPage-BIcWQun7.js.gz +0 -0
  473. package/dist/dashboard/assets/CliToolsPage-Bf2VelDe.js.gz +0 -0
  474. package/dist/dashboard/assets/CombosPage-DCgm5g6g.js +0 -1
  475. package/dist/dashboard/assets/CombosPage-DCgm5g6g.js.gz +0 -0
  476. package/dist/dashboard/assets/ConsoleLogPage-a8mD0QF1.js +0 -1
  477. package/dist/dashboard/assets/ConsoleLogPage-a8mD0QF1.js.gz +0 -0
  478. package/dist/dashboard/assets/CustomizationPage-ggeMSjte.js.gz +0 -0
  479. package/dist/dashboard/assets/HealthEventsModal-DzSWzE_j.js +0 -1
  480. package/dist/dashboard/assets/HealthEventsModal-DzSWzE_j.js.gz +0 -0
  481. package/dist/dashboard/assets/LoginPage-U6AKaJcF.js.gz +0 -0
  482. package/dist/dashboard/assets/ModelPicker-BMS2t8BA.js.gz +0 -0
  483. package/dist/dashboard/assets/OverviewPage-D2D4IiHl.js +0 -1
  484. package/dist/dashboard/assets/OverviewPage-D2D4IiHl.js.gz +0 -0
  485. package/dist/dashboard/assets/ProviderDetailPage-CrNf7j_X.js +0 -5
  486. package/dist/dashboard/assets/ProviderDetailPage-CrNf7j_X.js.gz +0 -0
  487. package/dist/dashboard/assets/ProviderIcon-FB6yS4v1.js +0 -1
  488. package/dist/dashboard/assets/ProviderIcon-FB6yS4v1.js.gz +0 -0
  489. package/dist/dashboard/assets/ProvidersPage-BSDbquc1.js +0 -1
  490. package/dist/dashboard/assets/ProvidersPage-BSDbquc1.js.gz +0 -0
  491. package/dist/dashboard/assets/ProxyPage-lsrx7NYg.js.gz +0 -0
  492. package/dist/dashboard/assets/QuotaPage-L0skzML8.js +0 -1
  493. package/dist/dashboard/assets/QuotaPage-L0skzML8.js.gz +0 -0
  494. package/dist/dashboard/assets/SettingsPage-CmAbjRHW.js +0 -1
  495. package/dist/dashboard/assets/SettingsPage-CmAbjRHW.js.gz +0 -0
  496. package/dist/dashboard/assets/SetupPage-f0DE1h0-.js.gz +0 -0
  497. package/dist/dashboard/assets/SortableList-ClzpwyET.js +0 -1
  498. package/dist/dashboard/assets/SortableList-ClzpwyET.js.gz +0 -0
  499. package/dist/dashboard/assets/StudioPage-Cq5tW7hW.js.gz +0 -0
  500. package/dist/dashboard/assets/UsagePage-Dfc9rWQn.js +0 -56
  501. package/dist/dashboard/assets/UsagePage-Dfc9rWQn.js.gz +0 -0
  502. package/dist/dashboard/assets/api-keys-C38iXoO4.js +0 -1
  503. package/dist/dashboard/assets/api-keys-C38iXoO4.js.gz +0 -0
  504. package/dist/dashboard/assets/app-a159k0ub.css +0 -2
  505. package/dist/dashboard/assets/app-a159k0ub.css.gz +0 -0
  506. package/dist/dashboard/assets/assert-DbRA8EG2.js +0 -15
  507. package/dist/dashboard/assets/assert-DbRA8EG2.js.gz +0 -0
  508. package/dist/dashboard/assets/badge-D9CS6dZk.js.gz +0 -0
  509. package/dist/dashboard/assets/banner-DympBNq4.js.gz +0 -0
  510. package/dist/dashboard/assets/card-ibRWU_uT.js.gz +0 -0
  511. package/dist/dashboard/assets/cli-tools-63qBGaRw.js +0 -1
  512. package/dist/dashboard/assets/cli-tools-63qBGaRw.js.gz +0 -0
  513. package/dist/dashboard/assets/clipboard-button-DJm-9pz7.js.gz +0 -0
  514. package/dist/dashboard/assets/dialog-8fiE6tec.js.gz +0 -0
  515. package/dist/dashboard/assets/drawer-CqGYeOrd.js.gz +0 -0
  516. package/dist/dashboard/assets/entry-BSAsYsmV.js.gz +0 -0
  517. package/dist/dashboard/assets/entry-CiIQULZD.js +0 -4
  518. package/dist/dashboard/assets/entry-CiIQULZD.js.gz +0 -0
  519. package/dist/dashboard/assets/entry-Daz0lJ-W.js +0 -1
  520. package/dist/dashboard/assets/entry-Daz0lJ-W.js.gz +0 -0
  521. package/dist/dashboard/assets/formatters-D-MTJxMr.js.gz +0 -0
  522. package/dist/dashboard/assets/github-badge-CPK47xQx.js.gz +0 -0
  523. package/dist/dashboard/assets/index-DBNPOfhV.js.gz +0 -0
  524. package/dist/dashboard/assets/inline-BT0zntEr.js.gz +0 -0
  525. package/dist/dashboard/assets/input-Da2ZViWg.js.gz +0 -0
  526. package/dist/dashboard/assets/layout-BXNeLw6M.js.gz +0 -0
  527. package/dist/dashboard/assets/list-row-BycOrrIV.js.gz +0 -0
  528. package/dist/dashboard/assets/live-DCQNZpF7.js +0 -1
  529. package/dist/dashboard/assets/live-DCQNZpF7.js.gz +0 -0
  530. package/dist/dashboard/assets/network-9Yj2ZyLo.js +0 -1
  531. package/dist/dashboard/assets/network-9Yj2ZyLo.js.gz +0 -0
  532. package/dist/dashboard/assets/numbers-CEagj6ZZ.js +0 -2
  533. package/dist/dashboard/assets/numbers-CEagj6ZZ.js.gz +0 -0
  534. package/dist/dashboard/assets/quota-Caw7wqci.js +0 -1
  535. package/dist/dashboard/assets/quota-Caw7wqci.js.gz +0 -0
  536. package/dist/dashboard/assets/routing-CO9zCUus.js +0 -1
  537. package/dist/dashboard/assets/routing-CO9zCUus.js.gz +0 -0
  538. package/dist/dashboard/assets/select-BIYIBkMU.js.gz +0 -0
  539. package/dist/dashboard/assets/settings-jS3-PNSA.js +0 -1
  540. package/dist/dashboard/assets/settings-jS3-PNSA.js.gz +0 -0
  541. package/dist/dashboard/assets/stack-DfgnFDWY.js.gz +0 -0
  542. package/dist/dashboard/assets/state-CkfdYWel.js.gz +0 -0
  543. package/dist/dashboard/assets/stream-retry-5QnVuSSo.js +0 -1
  544. package/dist/dashboard/assets/stream-retry-5QnVuSSo.js.gz +0 -0
  545. package/dist/dashboard/assets/switch-SYm237HG.js.gz +0 -0
  546. package/dist/dashboard/assets/useMutation-BQqCRNIi.js.gz +0 -0
  547. package/src/network/response-headers.ts +0 -39
  548. package/src/protocol/response/dsml.ts +0 -38
  549. package/src/providers/integrations/xiaomi-mimo/think-stream.ts +0 -229
  550. /package/dist/dashboard/assets/{app-BayTTy01.js → app-Bgd3zANQ.js} +0 -0
  551. /package/dist/dashboard/assets/{app-BayTTy01.js.gz → app-Bgd3zANQ.js.gz} +0 -0
  552. /package/dist/dashboard/assets/brand/{og_bansos.webp → og_bansos_initial.webp} +0 -0
  553. /package/dist/dashboard/assets/brand/{og_image.webp → og_image_initial.webp} +0 -0
@@ -6,20 +6,22 @@ import { canContainToolResult, joinTextParts, toolCallParts, toolResultParts } f
6
6
  import {
7
7
  classifyUpstreamError,
8
8
  extractUpstreamMessage,
9
+ mapUpstreamHttpError,
9
10
  upstreamErrorIdentifier,
10
11
  } from "../../../transport/failure-policy";
11
- import { createApiKeyAdapter } from "../../configured-provider";
12
- import type { CanonicalEvent, CanonicalRequest, CanonicalStopReason, ContentPart } from "../../../protocol/canonical-model";
12
+ import { postUpstreamJson } from "../../../protocol/upstream/post-json";
13
+ import type { CanonicalEvent, CanonicalRequest, CanonicalStopReason, ContentPart, UsageRecord } from "../../../protocol/canonical-model";
13
14
  import { isRecord, readNumber, readString, resolveImageSource, splitDataUrl } from "../../../protocol/codecs";
14
15
  import { usageFromProvider } from "../../provider-usage";
15
16
  import { readCredentialSecret, type ProviderDispatchTarget, type ProviderAdapter, type ProviderDispatchContext } from "../../provider-contracts";
17
+ import { abortGatewayError } from "../../operations/upstream-deadline";
16
18
  import { providerBaseUrl } from "../../provider-metadata";
17
19
  import { defineModel } from "../../provider-model-definition";
18
20
  import type { ModelDefinition } from "../../provider-model-definition";
19
21
  import {
20
22
  getCommandCodeVersion,
21
23
  } from "../../operations/client-versions";
22
- import { MimoThinkSplitter } from "../xiaomi-mimo/think-stream";
24
+ import { ThinkSplitter } from "../../../protocol/think-split";
23
25
  export const COMMANDCODE_PROVIDER_ID = "commandcode" as const;
24
26
  export const COMMANDCODE_BASE_URL = providerBaseUrl("commandcode");
25
27
  /**
@@ -37,10 +39,6 @@ export const COMMANDCODE_HOST = "https://api.commandcode.ai" as const;
37
39
  * generate.
38
40
  */
39
41
  export const COMMANDCODE_GENERATE_PATH = "/alpha/generate" as const;
40
- /** The three routes the Provider API serves, one per wire family. */
41
- export const COMMANDCODE_CHAT_PATH = "/chat/completions" as const;
42
- export const COMMANDCODE_RESPONSES_PATH = "/responses" as const;
43
- export const COMMANDCODE_MESSAGES_PATH = "/messages" as const;
44
42
 
45
43
  /**
46
44
  * Command Code's roster, as the Provider API publishes it.
@@ -426,7 +424,7 @@ export async function headers(sessionId: string, token: string): Promise<Record<
426
424
  }
427
425
 
428
426
 
429
- type StreamState = { toolIndexById: Map<string, number>; nextToolIndex: number; finishReason: string | undefined; usage: Record<string, unknown> | undefined; think: MimoThinkSplitter };
427
+ type StreamState = { toolIndexById: Map<string, number>; nextToolIndex: number; finishReason: string | undefined; usage: Record<string, unknown> | undefined; think: ThinkSplitter };
430
428
 
431
429
  export function ccMapFinishReason(reason: string): CanonicalStopReason {
432
430
  return reason === "length" ? "length" : reason === "tool_calls" ? "tool_use" : reason === "content_filter" ? "content_filter" : "stop";
@@ -450,7 +448,7 @@ export function transformLine(line: string, state: StreamState, seqBase: number)
450
448
  if (!text) return [];
451
449
  const split = state.think.push(text);
452
450
  const events: CanonicalEvent[] = [];
453
- if (split.reasoning) events.push({ type: "content_delta", sequence_number: seqBase, content: { kind: "reasoning", payload: null, summary: split.reasoning } } as CanonicalEvent);
451
+ if (split.reasoning) events.push({ type: "content_delta", sequence_number: seqBase++, content: { kind: "reasoning", payload: null, summary: split.reasoning } } as CanonicalEvent);
454
452
  if (split.text) events.push({ type: "content_delta", sequence_number: seqBase, content: { kind: "text", text: split.text } } as CanonicalEvent);
455
453
  return events;
456
454
  }
@@ -485,9 +483,11 @@ export function transformLine(line: string, state: StreamState, seqBase: number)
485
483
  if (type === "finish") {
486
484
  const reason = state.finishReason ?? readString(event, "finishReason") ?? "stop";
487
485
  const canonicalReason = ccMapFinishReason(reason);
488
- const events: CanonicalEvent[] = [
489
- { type: "terminal", sequence_number: seqBase, state: "complete", stop_reason: canonicalReason, provider_stop_reason: reason } as CanonicalEvent,
490
- ];
486
+ const rest = state.think.flush();
487
+ const events: CanonicalEvent[] = [];
488
+ if (rest.reasoning) events.push({ type: "content_delta", sequence_number: seqBase++, content: { kind: "reasoning", payload: null, summary: rest.reasoning } });
489
+ if (rest.text) events.push({ type: "content_delta", sequence_number: seqBase++, content: { kind: "text", text: rest.text } });
490
+ let usage: UsageRecord | undefined;
491
491
  if (state.usage) {
492
492
  const prompt = readNumber(state.usage, "promptTokens") ?? readNumber(state.usage, "inputTokens") ?? 0;
493
493
  const completion = readNumber(state.usage, "completionTokens") ?? readNumber(state.usage, "outputTokens") ?? 0;
@@ -500,9 +500,9 @@ export function transformLine(line: string, state: StreamState, seqBase: number)
500
500
  cached_tokens: cached,
501
501
  cache_creation_input_tokens: readNumber(state.usage, "cacheWriteTokens") ?? undefined,
502
502
  } as unknown as Record<string, unknown>);
503
- // Append usage to terminal
504
- (events[0] as unknown as Record<string, unknown>)["usage"] = usageRecord;
503
+ usage = usageRecord;
505
504
  }
505
+ events.push({ type: "terminal", sequence_number: seqBase, state: "complete", stop_reason: canonicalReason, provider_stop_reason: reason, ...(usage === undefined ? {} : { usage }) });
506
506
  return events;
507
507
  }
508
508
  if (type === "error") {
@@ -546,30 +546,29 @@ export function transformLine(line: string, state: StreamState, seqBase: number)
546
546
  return [];
547
547
  }
548
548
 
549
- /** NDJSON transform — beta-style simple loop + signal-aware read for abort/timeout propagation. */
550
- export async function* transformNdjson(body: ReadableStream<Uint8Array>, signal: AbortSignal): AsyncIterable<CanonicalEvent> {
549
+ /** NDJSON decoding uses the request signal after the opening deadline is released. */
550
+ export async function* transformNdjson(body: ReadableStream<Uint8Array>, signal: AbortSignal, model: string): AsyncIterable<CanonicalEvent> {
551
551
  const reader = body.getReader();
552
552
  const decoder = new TextDecoder();
553
- const state: StreamState = { toolIndexById: new Map(), nextToolIndex: 0, finishReason: undefined, usage: undefined, think: new MimoThinkSplitter() };
553
+ const state: StreamState = { toolIndexById: new Map(), nextToolIndex: 0, finishReason: undefined, usage: undefined, think: new ThinkSplitter() };
554
554
  let buffer = "";
555
- let hasTerminal = false;
556
555
  let seq = 1;
557
- // initial response_start
558
- yield { type: "response_start", sequence_number: seq++, model: "commandcode" } as CanonicalEvent;
559
556
 
560
- const readWithAbort = (): Promise<{ done: boolean; value?: Uint8Array }> => {
561
- if (signal.aborted) throw new GatewayError("transport_closed", 499, "Command Code stream aborted (request {requestId}). Send it again whenever ready 🙃");
562
- const { promise, resolve, reject } = Promise.withResolvers<{ done: boolean; value?: Uint8Array }>() as unknown as { promise: Promise<{ done: boolean; value?: Uint8Array }>; resolve: (v: unknown) => void; reject: (e: unknown) => void };
563
- const onAbort = (): void => {
564
- if (signal.aborted) reject(new GatewayError("transport_closed", 499, "Command Code stream aborted (request {requestId}). Send it again whenever ready 🙃"));
565
- else reject(new GatewayError("platform_unavailable", 502, "Command Code stream error (request {requestId}). The upstream came back empty-handed — retry 🌙", {}, "upstream"));
566
- };
557
+ const readWithAbort = async (): Promise<{ readonly done: boolean; readonly value?: Uint8Array | undefined }> => {
558
+ if (signal.aborted) throw signal.reason;
559
+ const { promise, resolve, reject } = Promise.withResolvers<{ readonly done: boolean; readonly value?: Uint8Array | undefined }>();
560
+ const onAbort = (): void => reject(signal.reason);
567
561
  signal.addEventListener("abort", onAbort, { once: true });
568
- void reader.read().then(resolve, reject).finally(() => signal.removeEventListener("abort", onAbort));
569
- return promise;
562
+ void reader.read().then(resolve, reject);
563
+ try {
564
+ return await promise;
565
+ } finally {
566
+ signal.removeEventListener("abort", onAbort);
567
+ }
570
568
  };
571
569
 
572
570
  try {
571
+ yield { type: "response_start", sequence_number: seq++, model };
573
572
  while (true) {
574
573
  const { done, value } = await readWithAbort();
575
574
  if (done) break;
@@ -579,20 +578,11 @@ export async function* transformNdjson(body: ReadableStream<Uint8Array>, signal:
579
578
  const line = buffer.slice(0, nl).trim();
580
579
  buffer = buffer.slice(nl + 1);
581
580
  if (line) {
582
- let evs: CanonicalEvent[] = [];
583
- try {
584
- evs = transformLine(line, state, seq);
585
- } catch (e) {
586
- throw e;
587
- }
588
- for (const ev of evs) {
589
- // Strictly increasing per line, in emission order: the Responses
590
- // surface throws on any non-monotonic sequence mid-stream, after
591
- // partial bytes are already flushed. No special-casing for
592
- // dual-event lines — order is order.
593
- (ev as unknown as Record<string, unknown>)["sequence_number"] = seq++;
594
- if (ev.type === "terminal") hasTerminal = true;
595
- yield ev;
581
+ const events = transformLine(line, state, seq);
582
+ seq += events.length;
583
+ for (const event of events) {
584
+ yield event;
585
+ if (event.type === "terminal") return;
596
586
  }
597
587
  }
598
588
  nl = buffer.indexOf("\n");
@@ -601,101 +591,71 @@ export async function* transformNdjson(body: ReadableStream<Uint8Array>, signal:
601
591
  buffer += decoder.decode();
602
592
  const tail = buffer.trim();
603
593
  if (tail) {
604
- for (const ev of transformLine(tail, state, seq)) {
605
- if (ev.type === "terminal") hasTerminal = true;
606
- (ev as unknown as Record<string, unknown>)["sequence_number"] = seq++;
607
- yield ev;
594
+ const events = transformLine(tail, state, seq);
595
+ seq += events.length;
596
+ for (const event of events) {
597
+ yield event;
598
+ if (event.type === "terminal") return;
608
599
  }
609
600
  }
610
601
  const rest = state.think.flush();
611
- if (rest.reasoning) yield { type: "content_delta", sequence_number: seq++, content: { kind: "reasoning", payload: null, summary: rest.reasoning } } as CanonicalEvent;
612
- if (rest.text) yield { type: "content_delta", sequence_number: seq++, content: { kind: "text", text: rest.text } } as CanonicalEvent;
613
- if (!hasTerminal) {
614
- // NDJSON ended with no terminal envelope: truncated, never complete.
615
- yield { type: "terminal", sequence_number: seq++, state: "failed", stop_reason: ccMapFinishReason(state.finishReason ?? "stop"), provider_stop_reason: state.finishReason ?? "stop" } as CanonicalEvent;
616
- }
602
+ if (rest.reasoning) yield { type: "content_delta", sequence_number: seq++, content: { kind: "reasoning", payload: null, summary: rest.reasoning } };
603
+ if (rest.text) yield { type: "content_delta", sequence_number: seq++, content: { kind: "text", text: rest.text } };
604
+ // NDJSON ended with no terminal envelope: truncated, never complete.
605
+ yield { type: "terminal", sequence_number: seq++, state: "failed", stop_reason: ccMapFinishReason(state.finishReason ?? "stop"), provider_stop_reason: state.finishReason ?? "stop" };
617
606
  } finally {
618
- try {
619
- reader.releaseLock?.();
620
- } catch {
621
- // Best-effort reader unlock during stream cleanup.
622
- }
623
- void reader.cancel().catch(() => {});
607
+ void reader.cancel(signal.reason).catch(() => {});
608
+ reader.releaseLock();
624
609
  }
625
610
  }
626
611
 
627
- // CommandCode model catalog.
628
-
629
-
630
-
631
- // ProviderAdapter — the Provider API serves three wires, so dispatch is the
632
- // shared OpenAI-compatible adapter rather than one bespoke NDJSON reader.
633
- //
634
- // Generation goes to `/alpha/generate` for every model: that is the CLI's own
635
- // surface and the one the reference client calls, and it does not vary the path
636
- // by the wire a model row declares. `/provider/v1` is the catalog surface — it
637
- // answers `GET /models`, which discovery still uses — so a completion posted to
638
- // `/provider/v1/<wire>` was reaching a surface the CLI never calls to
639
- // generate. The wire family still decides how the body is shaped; only the
640
- // endpoint is shared.
612
+ // The CLI generation surface takes a proprietary envelope and returns NDJSON
613
+ // regardless of the catalog row's wire family.
641
614
 
642
615
  interface CommandCodeAdapterOptions {
643
616
  readonly fetch?: typeof fetch;
644
617
  }
645
618
 
646
- function commandCodeIdentityHeaders(_token: string): Record<string, string> {
647
- return {
648
- "x-command-code-version": getCommandCodeVersion(),
649
- "x-cli-environment": "production",
650
- "x-taste-learning": "true",
651
- "x-project-slug": "cartethyia",
652
- "user-agent": "cli",
653
- // Per-request, as the CLI client sends it. The upstream expects a session
654
- // id on every generate call and the reference client mints a fresh UUID
655
- // for each one; sending a constant correlated requests that had nothing
656
- // to do with each other.
657
- "x-session-id": randomUUID(),
658
- };
659
- }
660
-
661
- function providerApiAdapter(options: CommandCodeAdapterOptions): ProviderAdapter {
662
- return createApiKeyAdapter(
663
- {
664
- provider_id: COMMANDCODE_PROVIDER_ID,
665
- // The host, not the `/provider/v1` base: generation is a sibling
666
- // surface, so every wire resolves under the host and the path below
667
- // supplies the CLI endpoint. Nesting it under `/provider/v1` produced a
668
- // URL that is neither surface.
669
- base_url: COMMANDCODE_HOST,
670
- endpoint_paths_by_wire_family: {
671
- // Every wire, not one per family: the reference client sends all
672
- // generation to `/alpha/generate` regardless of the row's declared
673
- // wire, so dispatch must not fan out across three paths.
674
- chat: COMMANDCODE_GENERATE_PATH,
675
- responses: COMMANDCODE_GENERATE_PATH,
676
- messages: COMMANDCODE_GENERATE_PATH,
677
- },
678
- buildExtraHeaders: async (context: ProviderDispatchContext) =>
679
- commandCodeIdentityHeaders(await readCredentialSecret(context.credential, "Command Code bearer token is required")),
680
- },
681
- options.fetch,
682
- );
683
- }
684
619
 
685
620
  class CommandCodeAdapter implements ProviderAdapter {
686
621
  readonly provider_id = COMMANDCODE_PROVIDER_ID;
687
- private readonly inner: ProviderAdapter;
622
+ private readonly fetchFn: typeof fetch;
688
623
 
689
624
  constructor(options: CommandCodeAdapterOptions = {}) {
690
- this.inner = providerApiAdapter(options);
625
+ this.fetchFn = options.fetch ?? globalThis.fetch;
691
626
  }
692
627
 
693
- dispatch(
628
+ async *dispatch(
694
629
  request: CanonicalRequest,
695
630
  candidate: ProviderDispatchTarget,
696
631
  context: ProviderDispatchContext,
697
632
  ): AsyncIterable<CanonicalEvent> {
698
- return this.inner.dispatch(request, candidate, context);
633
+ const token = readCredentialSecret(context.credential, "Command Code bearer token is required");
634
+ const sessionId = randomUUID();
635
+ const payload = buildRequest(candidate.model_id, {
636
+ messages: request.messages,
637
+ tools: (request.tools ?? []).map(({ name, description, jsonSchema }) => ({ name, description, jsonSchema })),
638
+ maxOutputTokens: request.generation_controls.max_output_tokens ?? request.generation_controls.max_tokens ?? request.generation_controls.max_completion_tokens ?? null,
639
+ temperature: request.generation_controls.temperature ?? null,
640
+ topP: request.generation_controls.top_p ?? null,
641
+ reasoningEffort: request.reasoning?.effort ?? null,
642
+ }, sessionId, [request.system, request.instructions].map((parts) => joinTextParts(parts ?? [])).filter(Boolean).join("\n"));
643
+ const { res, release } = await postUpstreamJson(
644
+ `${COMMANDCODE_HOST}${COMMANDCODE_GENERATE_PATH}`,
645
+ await headers(sessionId, token),
646
+ payload,
647
+ context,
648
+ context.outbound_fetch ?? this.fetchFn,
649
+ );
650
+ release();
651
+ try {
652
+ if (!res.ok) throw await mapUpstreamHttpError(res, COMMANDCODE_PROVIDER_ID);
653
+ if (!res.body) throw new GatewayError("platform_unavailable", 502, "Command Code returned an empty stream (request {requestId}). The upstream came back empty-handed — retry 🌙", {}, "upstream");
654
+ // NDJSON carries no model identity; use the selected model, not the provider id.
655
+ yield* transformNdjson(res.body, context.abort_signal, candidate.model_id);
656
+ } catch (error: unknown) {
657
+ throw abortGatewayError({ signal: context.abort_signal, release }, error) ?? error;
658
+ }
699
659
  }
700
660
  }
701
661
 
@@ -1,10 +1,8 @@
1
1
  /**
2
- * Command Code quota, read from the four billing endpoints its client reads.
2
+ * Command Code quota, read from the billing endpoints its client reads.
3
3
  *
4
- * Command Code bills in credits, not in rate-limit windows, so a quota row here
5
- * is a credit balance rather than a 5-hour/weekly pair. The shape the console
6
- * renders still expects windows, so the balance is reported as one independent
7
- * window — the honest reading, since credits are the only thing consumed.
4
+ * Monthly allowance and purchased/free credits are separate pooled windows:
5
+ * the monthly cap and reset do not limit or expire the additional balance.
8
6
  *
9
7
  * The endpoints, all on `https://api.commandcode.ai` (not the Provider API
10
8
  * origin the model catalog lives on):
@@ -12,17 +10,42 @@
12
10
  * `/alpha/whoami` identity, and the `orgId` when one exists
13
11
  * `/alpha/billing/credits` the balance and any window limits
14
12
  * `/alpha/billing/subscriptions` the plan, when there is one
15
- * `/alpha/usage/summary` spend in the period, for context
16
13
  *
17
- * Every call is optional to the result: a failure reports what is still known
18
- * rather than failing the whole read, because an operator who can see a zero
19
- * balance has already got the answer even if the usage summary never came back.
14
+ * Identity resolves the organization before billing is read. Billing reads are
15
+ * independent: retain the balance or subscription when the other is unavailable.
20
16
  */
21
17
  import type { FetchLike, ProviderQuotaResult, ProviderQuotaWindow } from "../../quota/quota-contracts";
22
18
 
23
19
  const COMMANDCODE_API_BASE = "https://api.commandcode.ai" as const;
24
20
  const REQUEST_TIMEOUT_MS = 15_000;
25
21
 
22
+ const PLAN_CAPS: Readonly<Record<string, number>> = {
23
+ "individual-go": 10,
24
+ "individual-goat": 70,
25
+ "individual-pro": 30,
26
+ "individual-provider": 15,
27
+ "individual-max": 150,
28
+ "individual-ultra": 300,
29
+ "teams-pro": 40,
30
+ };
31
+
32
+ const PLAN_CAP_OVERRIDES: Readonly<Record<string, number>> = {
33
+ "individual-pro-v1": 80,
34
+ };
35
+
36
+ function commandCodePlan(planId: string): { readonly label: string; readonly cap: number | null } {
37
+ const [scope, tier] = planId.toLowerCase().split("-");
38
+ if ((scope !== "individual" && scope !== "teams") || !tier) {
39
+ return { label: planId, cap: PLAN_CAP_OVERRIDES[planId] ?? PLAN_CAPS[planId] ?? null };
40
+ }
41
+ const tierLabel = tier === "goat" ? "GOAT" : `${tier[0]?.toUpperCase() ?? ""}${tier.slice(1)}`;
42
+ const normalizedId = `${scope}-${tier}`;
43
+ return {
44
+ label: `CommandCode ${scope === "teams" ? "Teams " : ""}${tierLabel}`,
45
+ cap: PLAN_CAP_OVERRIDES[planId] ?? PLAN_CAPS[normalizedId] ?? null,
46
+ };
47
+ }
48
+
26
49
  interface RequestOk {
27
50
  readonly ok: true;
28
51
  readonly value: unknown;
@@ -57,31 +80,69 @@ function recordValue(value: unknown): Record<string, unknown> | undefined {
57
80
  }
58
81
 
59
82
  function numberOrNull(value: unknown): number | null {
60
- return typeof value === "number" && Number.isFinite(value) ? value : null;
83
+ if (typeof value === "number" && Number.isFinite(value)) return value;
84
+ if (typeof value === "string" && value.trim() && Number.isFinite(Number(value))) {
85
+ return Number(value);
86
+ }
87
+ return null;
88
+ }
89
+
90
+ function isoTimestamp(value: unknown): string | null {
91
+ const numeric = numberOrNull(value);
92
+ if (numeric !== null) {
93
+ if (numeric <= 0) return null;
94
+ const date = new Date(numeric > 1_000_000_000_000 ? numeric : numeric * 1000);
95
+ return Number.isFinite(date.getTime()) ? date.toISOString() : null;
96
+ }
97
+ if (typeof value !== "string" || !Number.isFinite(Date.parse(value))) return null;
98
+ return new Date(value).toISOString();
61
99
  }
62
100
 
63
- /** The credit block: three balances that are summed, plus any window limits. */
64
- function creditWindow(credits: Record<string, unknown>): ProviderQuotaWindow | null {
101
+ function withQuery(path: string, params: Readonly<Record<string, string | null>>): string {
102
+ const query = new URLSearchParams(
103
+ Object.entries(params).filter((entry): entry is [string, string] => entry[1] !== null),
104
+ ).toString();
105
+ return query ? `${path}?${query}` : path;
106
+ }
107
+
108
+ /** Only monthly credits share the subscription allowance and reset. */
109
+ function creditWindows(
110
+ credits: Record<string, unknown>,
111
+ planCap: number | null,
112
+ resetsAt: string | null,
113
+ ): readonly ProviderQuotaWindow[] {
65
114
  const monthly = numberOrNull(credits["monthlyCredits"]) ?? 0;
66
115
  const purchased = numberOrNull(credits["purchasedCredits"]) ?? 0;
67
116
  const free = numberOrNull(credits["freeCredits"]) ?? 0;
68
- const remaining = monthly + purchased + free;
69
- // Command Code reports no ceiling for a credit balance, so there is no
70
- // percentage to compute — unlike a rate-limit window, where the upstream
71
- // states both sides. Reporting null is the truthful answer.
72
- return {
117
+ const used = planCap === null ? null : Math.max(0, planCap - monthly);
118
+ const usedPercent = planCap !== null && planCap > 0 && used !== null
119
+ ? Math.min(100, Math.max(0, (used / planCap) * 100))
120
+ : null;
121
+ const windows: ProviderQuotaWindow[] = [{
73
122
  kind: "credits",
74
- label: "Credits",
75
- // These are credits in one unit that can be spent interchangeably, so
76
- // they are pooled like Qoder's plan/granted balances — not independent
77
- // rate-limit lanes. The account-row pill and the Quota page both sum
78
- // pooled windows, so this must be `pooled` or the balance never renders.
123
+ label: "Monthly credits",
79
124
  accounting: "pooled",
80
- usedPercent: null,
81
- remainingPercent: null,
82
- resetsAt: null,
83
- remaining,
84
- };
125
+ usedPercent,
126
+ remainingPercent: usedPercent === null ? null : Math.max(0, 100 - usedPercent),
127
+ resetsAt,
128
+ used,
129
+ limit: planCap,
130
+ remaining: monthly,
131
+ }];
132
+ if (purchased + free > 0) {
133
+ windows.push({
134
+ kind: "additional_credits",
135
+ label: "Purchased/free credits",
136
+ accounting: "pooled",
137
+ usedPercent: null,
138
+ remainingPercent: null,
139
+ resetsAt: null,
140
+ used: null,
141
+ limit: null,
142
+ remaining: purchased + free,
143
+ });
144
+ }
145
+ return windows;
85
146
  }
86
147
 
87
148
  function windowLimits(
@@ -92,17 +153,25 @@ function windowLimits(
92
153
  const weekly = recordValue(limits["weekly"]);
93
154
  const push = (source: Record<string, unknown> | undefined, kind: string, label: string) => {
94
155
  if (source === undefined) return;
156
+ const used = numberOrNull(source["used"]) ?? 0;
157
+ const limit = numberOrNull(source["cap"]);
158
+ if ((limit === null || limit <= 0) && used <= 0) return;
159
+ const usedPercent = limit !== null && limit > 0
160
+ ? Math.min(100, Math.max(0, (used / limit) * 100))
161
+ : null;
95
162
  out.push({
96
163
  kind,
97
164
  label,
98
165
  accounting: "independent",
99
- usedPercent: numberOrNull(source["usedPercent"]),
100
- remainingPercent: numberOrNull(source["remainingPercent"]),
101
- resetsAt: typeof source["resetsAt"] === "string" ? source["resetsAt"] : null,
102
- remaining: numberOrNull(source["remaining"]),
166
+ usedPercent,
167
+ remainingPercent: usedPercent === null ? null : Math.max(0, 100 - usedPercent),
168
+ resetsAt: isoTimestamp(source["resetAt"]),
169
+ used,
170
+ limit,
171
+ remaining: limit === null ? null : Math.max(0, limit - used),
103
172
  });
104
173
  };
105
- push(fiveHour, "five_hour", "5 Hour");
174
+ push(fiveHour, "five_hour", "Session (5h)");
106
175
  push(weekly, "weekly", "Weekly");
107
176
  return out;
108
177
  }
@@ -112,12 +181,8 @@ export async function fetchCommandCodeQuota(
112
181
  fetcher: FetchLike,
113
182
  ): Promise<ProviderQuotaResult> {
114
183
  const source = "commandcode";
115
- const [whoami, creditsRaw, subscriptionRaw] = await Promise.all([
116
- readJson("/alpha/whoami", credential, fetcher),
117
- readJson("/alpha/billing/credits", credential, fetcher),
118
- readJson("/alpha/billing/subscriptions", credential, fetcher),
119
- ]);
120
- if (!whoami.ok && !creditsRaw.ok) {
184
+ const whoami = await readJson(withQuery("/alpha/whoami", { limits: "1" }), credential, fetcher);
185
+ if (!whoami.ok) {
121
186
  return {
122
187
  source,
123
188
  plan: null,
@@ -128,32 +193,47 @@ export async function fetchCommandCodeQuota(
128
193
 
129
194
  const whoamiBody = whoami.ok ? recordValue(whoami.value) : undefined;
130
195
  const user = recordValue(whoamiBody?.["user"]);
196
+ const organization = recordValue(whoamiBody?.["org"]);
197
+ const orgId = typeof organization?.["id"] === "string" ? organization["id"] : null;
131
198
  const accountLabel =
132
199
  (typeof user?.["email"] === "string" && user["email"]) ||
133
200
  (typeof user?.["userName"] === "string" && user["userName"]) ||
201
+ (typeof user?.["name"] === "string" && user["name"]) ||
134
202
  undefined;
135
203
 
204
+ const [creditsRaw, subscriptionRaw] = await Promise.all([
205
+ readJson(withQuery("/alpha/billing/credits", { orgId }), credential, fetcher),
206
+ readJson(withQuery("/alpha/billing/subscriptions", { orgId }), credential, fetcher),
207
+ ]);
208
+ if (!creditsRaw.ok && !subscriptionRaw.ok) {
209
+ return {
210
+ source,
211
+ plan: null,
212
+ windows: [],
213
+ error: "Command Code did not answer its billing endpoints",
214
+ ...(accountLabel === undefined ? {} : { accountLabel }),
215
+ };
216
+ }
217
+
218
+ const subscriptionBody = subscriptionRaw.ok ? recordValue(subscriptionRaw.value) : undefined;
219
+ const planRecord = recordValue(subscriptionBody?.["data"]);
220
+ const planId = typeof planRecord?.["planId"] === "string" ? planRecord["planId"] : null;
221
+ const parsedPlan = planId === null ? null : commandCodePlan(planId);
222
+ const plan =
223
+ parsedPlan?.label ??
224
+ (typeof planRecord?.["name"] === "string" ? planRecord["name"] : null) ??
225
+ (creditsRaw.ok ? "Credits" : null);
226
+ const periodEnd = isoTimestamp(planRecord?.["currentPeriodEnd"]);
227
+
136
228
  const creditsBody = creditsRaw.ok ? recordValue(creditsRaw.value) : undefined;
137
229
  const windows: ProviderQuotaWindow[] = [];
138
230
  if (creditsBody !== undefined) {
139
231
  const credits = recordValue(creditsBody["credits"]);
140
- if (credits !== undefined) {
141
- const window = creditWindow(credits);
142
- if (window !== null) windows.push(window);
143
- }
232
+ if (credits !== undefined) windows.push(...creditWindows(credits, parsedPlan?.cap ?? null, periodEnd));
144
233
  const limits = recordValue(creditsBody["windowLimits"]);
145
234
  if (limits !== undefined) windows.push(...windowLimits(limits));
146
235
  }
147
236
 
148
- const subscriptionBody = subscriptionRaw.ok ? recordValue(subscriptionRaw.value) : undefined;
149
- const planRecord = recordValue(subscriptionBody?.["data"]);
150
- const plan =
151
- typeof planRecord?.["name"] === "string"
152
- ? planRecord["name"]
153
- : creditsRaw.ok
154
- ? "Credits"
155
- : null;
156
-
157
237
  return {
158
238
  source,
159
239
  plan,
@@ -295,6 +295,3 @@ function concat(left: Uint8Array, right: Uint8Array): Uint8Array {
295
295
  out.set(right, left.byteLength);
296
296
  return out;
297
297
  }
298
-
299
- /** Heartbeat cadence while a Run is held open waiting for a tool result. */
300
- export const RUN_HEARTBEAT_INTERVAL_MS = 20_000;
@@ -35,6 +35,7 @@ import {
35
35
  } from "../../quota/quota-contracts";
36
36
  import type { QuotaCollectionContext } from "../../quota/quota-support";
37
37
  import { cursorAccountIdentity } from "./cursor-auth";
38
+ import { fnv1a32 } from "../shared/hash";
38
39
  import { CURSOR_API_HOST, CURSOR_WEBSITE_HOST } from "./cursor-shared";
39
40
 
40
41
  /** Timestamp keys naming the end of the current window. */
@@ -226,12 +227,7 @@ function authStateUserId(context: QuotaCollectionContext | undefined): string |
226
227
  * subject it replaces.
227
228
  */
228
229
  function cursorAccountTag(credential: string): string {
229
- let hash = 0x811c9dc5;
230
- for (let index = 0; index < credential.length; index += 1) {
231
- hash ^= credential.charCodeAt(index);
232
- hash = Math.imul(hash, 0x01000193) >>> 0;
233
- }
234
- return `cursor-${hash.toString(36).padStart(7, "0").slice(0, 7)}`;
230
+ return `cursor-${fnv1a32(credential).toString(36).padStart(7, "0").slice(0, 7)}`;
235
231
  }
236
232
 
237
233
  export async function fetchCursorQuota(