cartethyia 2.7.4 → 2.7.6

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 (595) hide show
  1. package/.env.example +5 -0
  2. package/README.md +18 -6
  3. package/dist/dashboard/assets/BannedPage-B1N603cM.js +1 -0
  4. package/dist/dashboard/assets/BannedPage-B1N603cM.js.gz +0 -0
  5. package/dist/dashboard/assets/CliToolDetailPage-9q4_4NoK.js +3 -0
  6. package/dist/dashboard/assets/CliToolDetailPage-9q4_4NoK.js.gz +0 -0
  7. package/dist/dashboard/assets/CliToolsPage-DwshGtK-.js +1 -0
  8. package/dist/dashboard/assets/CliToolsPage-DwshGtK-.js.gz +0 -0
  9. package/dist/dashboard/assets/CombosPage-D762MIkp.js +1 -0
  10. package/dist/dashboard/assets/CombosPage-D762MIkp.js.gz +0 -0
  11. package/dist/dashboard/assets/ConsoleLogPage-KRYjHZsX.js +1 -0
  12. package/dist/dashboard/assets/ConsoleLogPage-KRYjHZsX.js.gz +0 -0
  13. package/dist/dashboard/assets/CustomizationPage-DXsxk5uM.js +1 -0
  14. package/dist/dashboard/assets/CustomizationPage-DXsxk5uM.js.gz +0 -0
  15. package/dist/dashboard/assets/HealthEventsModal-BEjAjD4F.js +1 -0
  16. package/dist/dashboard/assets/HealthEventsModal-BEjAjD4F.js.gz +0 -0
  17. package/dist/dashboard/assets/InjectSystemPromptPanel-d5v3K_Yc.js +899 -0
  18. package/dist/dashboard/assets/InjectSystemPromptPanel-d5v3K_Yc.js.gz +0 -0
  19. package/dist/dashboard/assets/LoginPage-Ccv4tjNS.js +1 -0
  20. package/dist/dashboard/assets/LoginPage-Ccv4tjNS.js.gz +0 -0
  21. package/dist/dashboard/assets/{ModelPicker-BMS2t8BA.js → ModelPicker-C6WlpU5E.js} +1 -1
  22. package/dist/dashboard/assets/ModelPicker-C6WlpU5E.js.gz +0 -0
  23. package/dist/dashboard/assets/OverviewPage-DN9fYNWA.js +1 -0
  24. package/dist/dashboard/assets/OverviewPage-DN9fYNWA.js.gz +0 -0
  25. package/dist/dashboard/assets/ProviderDetailPage-BY0ieUgz.js +5 -0
  26. package/dist/dashboard/assets/ProviderDetailPage-BY0ieUgz.js.gz +0 -0
  27. package/dist/dashboard/assets/ProviderIcon-jjqXJDA3.js +1 -0
  28. package/dist/dashboard/assets/ProviderIcon-jjqXJDA3.js.gz +0 -0
  29. package/dist/dashboard/assets/ProvidersPage-DNsbfea2.js +1 -0
  30. package/dist/dashboard/assets/ProvidersPage-DNsbfea2.js.gz +0 -0
  31. package/dist/dashboard/assets/{ProxyPage-lsrx7NYg.js → ProxyPage-DxjvUzSD.js} +1 -1
  32. package/dist/dashboard/assets/ProxyPage-DxjvUzSD.js.gz +0 -0
  33. package/dist/dashboard/assets/QuotaPage-OaHScjpX.js +1 -0
  34. package/dist/dashboard/assets/QuotaPage-OaHScjpX.js.gz +0 -0
  35. package/dist/dashboard/assets/SettingsPage-BW4AKj2H.js +1 -0
  36. package/dist/dashboard/assets/SettingsPage-BW4AKj2H.js.gz +0 -0
  37. package/dist/dashboard/assets/SetupPage-44BZrxFC.js +1 -0
  38. package/dist/dashboard/assets/SetupPage-44BZrxFC.js.gz +0 -0
  39. package/dist/dashboard/assets/{StudioPage-Cq5tW7hW.js → StudioPage-DRPavCdU.js} +4 -4
  40. package/dist/dashboard/assets/StudioPage-DRPavCdU.js.gz +0 -0
  41. package/dist/dashboard/assets/ToolIcon-CfeLA-Z_.js +1 -0
  42. package/dist/dashboard/assets/ToolIcon-CfeLA-Z_.js.gz +0 -0
  43. package/dist/dashboard/assets/UsagePage-B7M7LvMM.js +56 -0
  44. package/dist/dashboard/assets/UsagePage-B7M7LvMM.js.gz +0 -0
  45. package/dist/dashboard/assets/api-keys-KvX83mRe.js +1 -0
  46. package/dist/dashboard/assets/api-keys-KvX83mRe.js.gz +0 -0
  47. package/dist/dashboard/assets/app-DRnQLhsc.js +9 -0
  48. package/dist/dashboard/assets/app-DRnQLhsc.js.gz +0 -0
  49. package/dist/dashboard/assets/app-mweRcsKp.css +2 -0
  50. package/dist/dashboard/assets/app-mweRcsKp.css.gz +0 -0
  51. package/dist/dashboard/assets/assert-CidQVoNo.js +1 -0
  52. package/dist/dashboard/assets/assert-CidQVoNo.js.gz +0 -0
  53. package/dist/dashboard/assets/{badge-D9CS6dZk.js → badge-BaX2GTrn.js} +1 -1
  54. package/dist/dashboard/assets/badge-BaX2GTrn.js.gz +0 -0
  55. package/dist/dashboard/assets/{banner-DympBNq4.js → banner-DFH4Bkte.js} +1 -1
  56. package/dist/dashboard/assets/banner-DFH4Bkte.js.gz +0 -0
  57. package/dist/dashboard/assets/brand/login_register.jpg +0 -0
  58. package/dist/dashboard/assets/brand/login_register_initial.jpg +0 -0
  59. package/dist/dashboard/assets/brand/og_bansos.jpg +0 -0
  60. package/dist/dashboard/assets/brand/og_image.jpg +0 -0
  61. package/dist/dashboard/assets/{card-ibRWU_uT.js → card-CxDw0lK6.js} +1 -1
  62. package/dist/dashboard/assets/card-CxDw0lK6.js.gz +0 -0
  63. package/dist/dashboard/assets/chevron-left-Bf6CaTSJ.js +1 -0
  64. package/dist/dashboard/assets/chevron-left-Bf6CaTSJ.js.gz +0 -0
  65. package/dist/dashboard/assets/circle-question-mark-Cz4un8sH.js +1 -0
  66. package/dist/dashboard/assets/circle-question-mark-Cz4un8sH.js.gz +0 -0
  67. package/dist/dashboard/assets/cli-tools-Bwv5-zGZ.js +1 -0
  68. package/dist/dashboard/assets/cli-tools-Bwv5-zGZ.js.gz +0 -0
  69. package/dist/dashboard/assets/{clipboard-button-DJm-9pz7.js → clipboard-button-DfS1ntiq.js} +1 -1
  70. package/dist/dashboard/assets/clipboard-button-DfS1ntiq.js.gz +0 -0
  71. package/dist/dashboard/assets/dialog-BK-NRr5u.js +1 -0
  72. package/dist/dashboard/assets/dialog-BK-NRr5u.js.gz +0 -0
  73. package/dist/dashboard/assets/{drawer-CqGYeOrd.js → drawer-QqPul6sC.js} +1 -1
  74. package/dist/dashboard/assets/drawer-QqPul6sC.js.gz +0 -0
  75. package/dist/dashboard/assets/entry-BMlYJVFj.js +1 -0
  76. package/dist/dashboard/assets/entry-BMlYJVFj.js.gz +0 -0
  77. package/dist/dashboard/assets/entry-D6WvojeD.js +4 -0
  78. package/dist/dashboard/assets/entry-D6WvojeD.js.gz +0 -0
  79. package/dist/dashboard/assets/entry-Dmx7gBiN.js +1 -0
  80. package/dist/dashboard/assets/entry-Dmx7gBiN.js.gz +0 -0
  81. package/dist/dashboard/assets/{formatters-D-MTJxMr.js → formatters-u7JPB5BX.js} +1 -1
  82. package/dist/dashboard/assets/formatters-u7JPB5BX.js.gz +0 -0
  83. package/dist/dashboard/assets/house-h9AZlJuT.js +1 -0
  84. package/dist/dashboard/assets/house-h9AZlJuT.js.gz +0 -0
  85. package/dist/dashboard/assets/{index-DBNPOfhV.js → index-Ko4dLRnV.js} +2 -2
  86. package/dist/dashboard/assets/index-Ko4dLRnV.js.gz +0 -0
  87. package/dist/dashboard/assets/{inline-BT0zntEr.js → inline-CHJQwxLa.js} +1 -1
  88. package/dist/dashboard/assets/inline-CHJQwxLa.js.gz +0 -0
  89. package/dist/dashboard/assets/{input-Da2ZViWg.js → input-BlmlizVV.js} +1 -1
  90. package/dist/dashboard/assets/input-BlmlizVV.js.gz +0 -0
  91. package/dist/dashboard/assets/{layout-BXNeLw6M.js → layout-Bz93I-yE.js} +1 -1
  92. package/dist/dashboard/assets/layout-Bz93I-yE.js.gz +0 -0
  93. package/dist/dashboard/assets/{list-row-BycOrrIV.js → list-row-Ml16KCfu.js} +1 -1
  94. package/dist/dashboard/assets/list-row-Ml16KCfu.js.gz +0 -0
  95. package/dist/dashboard/assets/live-7_QpnYGu.js +1 -0
  96. package/dist/dashboard/assets/live-7_QpnYGu.js.gz +0 -0
  97. package/dist/dashboard/assets/{mutation-Rr0TIvcj.js → mutation-GU7XdEjz.js} +1 -1
  98. package/dist/dashboard/assets/mutation-GU7XdEjz.js.gz +0 -0
  99. package/dist/dashboard/assets/network-CuMDaTzT.js +1 -0
  100. package/dist/dashboard/assets/network-CuMDaTzT.js.gz +0 -0
  101. package/dist/dashboard/assets/numbers-CoqpaDMx.js +2 -0
  102. package/dist/dashboard/assets/numbers-CoqpaDMx.js.gz +0 -0
  103. package/dist/dashboard/assets/pagination-B5aOoweZ.js +1 -0
  104. package/dist/dashboard/assets/pagination-B5aOoweZ.js.gz +0 -0
  105. package/dist/dashboard/assets/providers/zai.webp +0 -0
  106. package/dist/dashboard/assets/providers/zcode.webp +0 -0
  107. package/dist/dashboard/assets/{query-keys-BloKtaXJ.js → query-keys-YSoQOUh6.js} +1 -1
  108. package/dist/dashboard/assets/{query-keys-BloKtaXJ.js.gz → query-keys-YSoQOUh6.js.gz} +0 -0
  109. package/dist/dashboard/assets/quota-DJ1QlAOq.js +1 -0
  110. package/dist/dashboard/assets/quota-DJ1QlAOq.js.gz +0 -0
  111. package/dist/dashboard/assets/routing-fnCsxgNO.js +1 -0
  112. package/dist/dashboard/assets/routing-fnCsxgNO.js.gz +0 -0
  113. package/dist/dashboard/assets/{select-BIYIBkMU.js → select-BHqpd-bg.js} +2 -2
  114. package/dist/dashboard/assets/select-BHqpd-bg.js.gz +0 -0
  115. package/dist/dashboard/assets/settings-BxslsErn.js +1 -0
  116. package/dist/dashboard/assets/settings-BxslsErn.js.gz +0 -0
  117. package/dist/dashboard/assets/shield-BMQNxORh.js +1 -0
  118. package/dist/dashboard/assets/shield-BMQNxORh.js.gz +0 -0
  119. package/dist/dashboard/assets/{stack-DfgnFDWY.js → stack-j7cXjgYX.js} +1 -1
  120. package/dist/dashboard/assets/stack-j7cXjgYX.js.gz +0 -0
  121. package/dist/dashboard/assets/{state-CkfdYWel.js → state-DHmEnLa2.js} +1 -1
  122. package/dist/dashboard/assets/state-DHmEnLa2.js.gz +0 -0
  123. package/dist/dashboard/assets/{switch-SYm237HG.js → switch-DmZcyTIC.js} +1 -1
  124. package/dist/dashboard/assets/switch-DmZcyTIC.js.gz +0 -0
  125. package/dist/dashboard/assets/{useMutation-BQqCRNIi.js → useMutation-CbCP4w1o.js} +1 -1
  126. package/dist/dashboard/assets/useMutation-CbCP4w1o.js.gz +0 -0
  127. package/dist/dashboard/console-backgrounds.jpg +0 -0
  128. package/dist/dashboard/console-backgrounds_initial.jpg +0 -0
  129. package/dist/dashboard/index.html +2 -2
  130. package/dist/dashboard/index.html.gz +0 -0
  131. package/dist/dashboard/precompressed-assets.json +60 -59
  132. package/migrations/full-postgres/0036_system_prompt_presets_refs.sql +10 -0
  133. package/migrations/full-postgres/0037_share_concurrency_scope.sql +3 -0
  134. package/migrations/full-postgres/0038_add_accounts_quota_snapshot.sql +7 -0
  135. package/migrations/full-postgres/0039_add_telemetry_provider_model_indexes.sql +13 -0
  136. package/migrations/full-postgres/0040_add_models_dialect.sql +3 -0
  137. package/migrations/full-postgres/0041_add_daily_checkin_ledger_account_fk.sql +7 -0
  138. package/migrations/full-postgres/0042_add_telemetry_degenerate_stop.sql +14 -0
  139. package/migrations/lite-sqlite3/0036_system_prompt_presets_refs.sql +14 -0
  140. package/migrations/lite-sqlite3/0037_share_concurrency_scope.sql +3 -0
  141. package/migrations/lite-sqlite3/0038_add_accounts_quota_snapshot.sql +4 -0
  142. package/migrations/lite-sqlite3/0039_add_telemetry_provider_model_indexes.sql +6 -0
  143. package/migrations/lite-sqlite3/0040_add_models_dialect.sql +3 -0
  144. package/migrations/lite-sqlite3/0041_add_daily_checkin_ledger_account_fk.sql +26 -0
  145. package/migrations/lite-sqlite3/0042_add_telemetry_degenerate_stop.sql +12 -0
  146. package/package.json +4 -6
  147. package/scripts/cli/update.ts +15 -41
  148. package/src/boot/app.ts +58 -2
  149. package/src/boot/dependencies.ts +794 -732
  150. package/src/boot/lifecycle.ts +11 -12
  151. package/src/config/cooldowns.ts +34 -55
  152. package/src/config/network.ts +1 -1
  153. package/src/config/package-paths.ts +1 -1
  154. package/src/config/providers.ts +19 -14
  155. package/src/config/resources.ts +0 -5
  156. package/src/config/runtime.ts +2 -5
  157. package/src/config/spec.ts +33 -6
  158. package/src/config/usage.ts +27 -0
  159. package/src/console/api-keys/ban-constants.ts +38 -0
  160. package/src/console/api-keys/contracts.ts +71 -62
  161. package/src/console/api-keys/routes.ts +56 -24
  162. package/src/console/auth/access.ts +4 -4
  163. package/src/console/auth/account-reset.ts +1 -1
  164. package/src/console/auth/audit-recorder.ts +1 -1
  165. package/src/console/auth/lockout.ts +7 -3
  166. package/src/console/auth/row-coercion.ts +57 -12
  167. package/src/console/auth/session-resolver.ts +1 -1
  168. package/src/console/auth/session-service.ts +8 -2
  169. package/src/console/auth/session.ts +37 -31
  170. package/src/console/auth/setup-service.ts +4 -3
  171. package/src/console/auth/user-store.ts +1 -1
  172. package/src/console/backup/9router-map.ts +0 -3
  173. package/src/console/backup/contracts.ts +9 -9
  174. package/src/console/backup/export-endpoint.ts +5 -6
  175. package/src/console/backup/nine-router.ts +1 -7
  176. package/src/console/backup/routes.ts +29 -33
  177. package/src/console/backup/service.ts +468 -468
  178. package/src/console/backup/store.ts +47 -29
  179. package/src/console/backup/validate.ts +11 -1
  180. package/src/console/cli-tools/contracts.ts +43 -299
  181. package/src/console/cli-tools/fs-ops.ts +29 -0
  182. package/src/console/cli-tools/injectors/claude.ts +3 -3
  183. package/src/console/cli-tools/injectors/codex.ts +181 -181
  184. package/src/console/cli-tools/injectors/driver.ts +150 -247
  185. package/src/console/cli-tools/injectors/kilo.ts +146 -21
  186. package/src/console/cli-tools/injectors/opencode.ts +176 -153
  187. package/src/console/cli-tools/routes.ts +7 -1
  188. package/src/console/cli-tools/service.ts +8 -7
  189. package/src/console/cli-tools/store.ts +2 -2
  190. package/src/console/console-router.ts +32 -0
  191. package/src/console/dashboard-assets.ts +9 -9
  192. package/src/console/domain-registration.ts +16 -2
  193. package/src/console/inferhub/client.ts +0 -7
  194. package/src/console/inferhub/key-store.ts +1 -1
  195. package/src/console/inferhub/normalize.ts +2 -2
  196. package/src/console/inferhub/routes.ts +1 -1
  197. package/src/console/inferhub/types.ts +1 -1
  198. package/src/console/observability/contracts.ts +7 -1
  199. package/src/console/observability/live.ts +31 -18
  200. package/src/console/observability/logs.ts +1 -1
  201. package/src/console/observability/sse.ts +62 -8
  202. package/src/console/observability/store.ts +23 -4
  203. package/src/console/providers/catalog/catalog-projections.ts +1 -1
  204. package/src/console/providers/catalog/contracts.ts +39 -0
  205. package/src/console/providers/catalog/model-operations.ts +1 -1
  206. package/src/console/providers/catalog/provider-operations.ts +95 -17
  207. package/src/console/providers/catalog/public-model-store.ts +0 -0
  208. package/src/console/providers/catalog/routes.ts +27 -3
  209. package/src/console/providers/catalog/store.ts +147 -8
  210. package/src/console/providers/detail/routes.ts +1 -1
  211. package/src/console/providers/oauth/callback-listener.ts +100 -18
  212. package/src/console/providers/oauth/routes.ts +92 -13
  213. package/src/console/quota/account-quota-global.ts +41 -3
  214. package/src/console/quota/account-quota-shared.ts +10 -3
  215. package/src/console/quota/account-quota-tenant.ts +259 -66
  216. package/src/console/quota/daily-growth-job.ts +308 -0
  217. package/src/console/quota/native-identity.ts +2 -2
  218. package/src/console/quota/quota-view.ts +1 -1
  219. package/src/console/remote-support/routes.ts +1 -1
  220. package/src/console/remote-support/service.ts +2 -2
  221. package/src/console/routing/model/contracts.ts +1 -1
  222. package/src/console/routing/pools/contracts.ts +2 -2
  223. package/src/console/routing/pools/import-parse.ts +3 -3
  224. package/src/console/routing/pools/relay-deploy.ts +3 -3
  225. package/src/console/settings/contracts.ts +29 -2
  226. package/src/console/settings/store.ts +20 -0
  227. package/src/console/share/share-router.ts +100 -32
  228. package/src/console/share/share-stats.ts +11 -13
  229. package/src/console/share/share-usage.ts +1 -1
  230. package/src/console/shared/errors.ts +2 -2
  231. package/src/console/shared/object.ts +18 -0
  232. package/src/console/shared/read-through-cache.ts +4 -4
  233. package/src/console/shared/transaction.ts +14 -0
  234. package/src/console/system-prompt/contracts.ts +89 -12
  235. package/src/console/system-prompt/routes.ts +98 -45
  236. package/src/console/updates/apply-update.ts +244 -0
  237. package/src/console/updates/routes.ts +252 -97
  238. package/src/main.ts +11 -5
  239. package/src/network/http2-bidi.ts +1 -1
  240. package/src/network/ip-utils.ts +298 -0
  241. package/src/network/outbound-fetch.ts +1 -1
  242. package/src/network/pool/agent.ts +7 -6
  243. package/src/network/pool/byte-accounting.ts +1 -1
  244. package/src/network/pool/resolver.ts +16 -12
  245. package/src/network/pool/selector.ts +150 -136
  246. package/src/network/pool-health-machine.ts +1 -1
  247. package/src/network/rate-limit-verdict.ts +1 -1
  248. package/src/network/retry.ts +1 -1
  249. package/src/network/ssrf.ts +20 -71
  250. package/src/observability/event-log.ts +9 -2
  251. package/src/observability/event-query.ts +61 -291
  252. package/src/observability/log-sanitize.ts +1 -1
  253. package/src/observability/metrics.ts +4 -4
  254. package/src/observability/payload-capture.ts +6 -5
  255. package/src/observability/payload-store.ts +1 -1
  256. package/src/observability/runtime-metrics.ts +1 -1
  257. package/src/observability/telemetry-buffer.ts +19 -3
  258. package/src/observability/telemetry-reconciliation.ts +3 -3
  259. package/src/observability/token-speed.ts +1 -1
  260. package/src/persistence/connection-url.ts +1 -1
  261. package/src/persistence/db-handle.ts +1 -1
  262. package/src/persistence/db-pg.ts +2 -2
  263. package/src/persistence/db-sqlite.ts +3 -3
  264. package/src/persistence/dialect.ts +5 -1
  265. package/src/persistence/readiness.ts +1 -1
  266. package/src/persistence/redis.ts +1 -1
  267. package/src/persistence/schema.ts +121 -40
  268. package/src/persistence/shared-cache.ts +2 -2
  269. package/src/persistence/sort-order.ts +1 -1
  270. package/src/persistence/stores/api-key-store.ts +29 -2
  271. package/src/persistence/stores/share-store.ts +20 -8
  272. package/src/persistence/system-prompt-store.ts +63 -18
  273. package/src/persistence/tenant-preferences.ts +1 -1
  274. package/src/persistence/ttl-cache.ts +3 -3
  275. package/src/persistence/usage-flush.ts +7 -2
  276. package/src/protocol/canonical-model.ts +1 -1
  277. package/src/protocol/codecs.ts +6 -10
  278. package/src/protocol/dsml.ts +8 -125
  279. package/src/protocol/image-clamp.ts +2 -2
  280. package/src/protocol/reasoning-text.ts +1 -1
  281. package/src/protocol/reasoning.ts +7 -52
  282. package/src/protocol/registry.ts +50 -15
  283. package/src/protocol/request/chat.ts +46 -19
  284. package/src/protocol/request/codex.ts +1 -1
  285. package/src/protocol/request/messages.ts +46 -14
  286. package/src/protocol/request/responses.ts +19 -10
  287. package/src/protocol/response/chat.ts +25 -10
  288. package/src/protocol/response/codex.ts +1 -1
  289. package/src/protocol/response/messages.ts +1 -1
  290. package/src/protocol/response/omp-tool-call-markup.ts +1 -1
  291. package/src/protocol/response/responses.ts +1 -1
  292. package/src/protocol/sse-decoder.ts +50 -1
  293. package/src/protocol/stop-reason.ts +2 -2
  294. package/src/protocol/think-split.ts +347 -0
  295. package/src/protocol/translation/attachment-integrity.ts +3 -3
  296. package/src/protocol/translation/cache-controls.ts +8 -20
  297. package/src/protocol/translation/capabilities.ts +6 -6
  298. package/src/protocol/translation/dialect/deepseek.ts +10 -0
  299. package/src/protocol/translation/dialect/inband.ts +39 -0
  300. package/src/protocol/translation/dialect/model-thinking.ts +89 -0
  301. package/src/protocol/translation/dialect/qwen.ts +114 -0
  302. package/src/protocol/translation/dialect/resolver.ts +83 -0
  303. package/src/protocol/translation/dialect/rules.ts +93 -0
  304. package/src/protocol/translation/dialect/thinking-ladder.ts +245 -0
  305. package/src/protocol/translation/dialect/types.ts +58 -0
  306. package/src/protocol/translation/quirks.ts +18 -4
  307. package/src/protocol/translation/thinking.ts +12 -252
  308. package/src/protocol/translation/tool-id.ts +3 -3
  309. package/src/protocol/translation/tool-repair.ts +1 -1
  310. package/src/protocol/upstream/post-json.ts +6 -1
  311. package/src/providers/adapters/compatible-adapter.ts +71 -4
  312. package/src/providers/authentication/jwt-validator.ts +0 -5
  313. package/src/providers/authentication/oauth-client.ts +254 -253
  314. package/src/providers/authentication/oauth-flow-store.ts +42 -7
  315. package/src/providers/bundled-discovery-providers.ts +6 -30
  316. package/src/providers/configured-openai-providers.ts +82 -97
  317. package/src/providers/default-registry.ts +35 -37
  318. package/src/providers/discovery/catalog/catalog-format.ts +13 -2
  319. package/src/providers/discovery/discovery-types.ts +0 -1
  320. package/src/providers/discovery/openai-model-discovery.ts +2 -2
  321. package/src/providers/discovery/probe-phases.ts +48 -13
  322. package/src/providers/discovery/probing-service.ts +6 -8
  323. package/src/providers/integrations/agentrouter/agentrouter.ts +12 -2
  324. package/src/providers/integrations/antigravity/antigravity-oauth.ts +0 -1
  325. package/src/providers/integrations/antigravity/antigravity-protocol.ts +88 -28
  326. package/src/providers/integrations/antigravity/antigravity-quota.ts +18 -6
  327. package/src/providers/integrations/antigravity/antigravity.ts +22 -13
  328. package/src/providers/integrations/buddy/buddy-catalog-shared.ts +1 -1
  329. package/src/providers/integrations/buddy/buddy-chat-shared.ts +20 -4
  330. package/src/providers/integrations/buddy/buddy-cli-headers.ts +19 -52
  331. package/src/providers/integrations/buddy/buddy-identity.ts +86 -0
  332. package/src/providers/integrations/buddy/buddy-oauth-shared.ts +1 -1
  333. package/src/providers/integrations/buddy/codebuddy/cn-provider.ts +1 -2
  334. package/src/providers/integrations/buddy/codebuddy/provider.ts +4 -15
  335. package/src/providers/integrations/buddy/codebuddy/shared.ts +30 -9
  336. package/src/providers/integrations/buddy/workbuddy/provider.ts +5 -9
  337. package/src/providers/integrations/buddy/workbuddy/shared.ts +21 -3
  338. package/src/providers/integrations/claude/claude-cch.ts +148 -148
  339. package/src/providers/integrations/claude/fallback-credit.ts +3 -7
  340. package/src/providers/integrations/cline/cline.ts +1 -1
  341. package/src/providers/integrations/codex/codex-device-code.ts +101 -101
  342. package/src/providers/integrations/codex/codex-oauth.ts +0 -1
  343. package/src/providers/integrations/commandcode/commandcode.ts +75 -115
  344. package/src/providers/integrations/commandcode/quota.ts +131 -51
  345. package/src/providers/integrations/cursor/cursor-http2.ts +0 -3
  346. package/src/providers/integrations/cursor/cursor-quota.ts +2 -6
  347. package/src/providers/integrations/cursor/cursor-request.ts +0 -87
  348. package/src/providers/integrations/cursor/cursor-shared.ts +0 -4
  349. package/src/providers/integrations/cursor/cursor.ts +35 -3
  350. package/src/providers/integrations/cursor/protocol/framing.ts +0 -56
  351. package/src/providers/integrations/cursor/protocol/messages.ts +0 -96
  352. package/src/providers/integrations/factory/factory.ts +9 -9
  353. package/src/providers/integrations/github/github-discovery.ts +70 -12
  354. package/src/providers/integrations/github/github-oauth.ts +184 -14
  355. package/src/providers/integrations/github/github.ts +134 -26
  356. package/src/providers/integrations/grok/grok-oauth.ts +261 -261
  357. package/src/providers/integrations/grok/grok-quota-frame.ts +1 -1
  358. package/src/providers/integrations/grok/grok-quota.ts +16 -7
  359. package/src/providers/integrations/grok/grok-ratelimit-store.ts +0 -5
  360. package/src/providers/integrations/grok/grok-ratelimit.ts +3 -0
  361. package/src/providers/integrations/grok/grok.ts +86 -36
  362. package/src/providers/integrations/kilo/kilo-oauth.ts +1 -1
  363. package/src/providers/integrations/kiro/kiro-cache.ts +31 -0
  364. package/src/providers/integrations/kiro/kiro-catalog.ts +31 -5
  365. package/src/providers/integrations/kiro/kiro-discovery.ts +47 -43
  366. package/src/providers/integrations/kiro/kiro-machine-id.ts +6 -3
  367. package/src/providers/integrations/kiro/kiro-oauth.ts +523 -255
  368. package/src/providers/integrations/kiro/kiro-quota.ts +35 -47
  369. package/src/providers/integrations/kiro/kiro-request.ts +8 -11
  370. package/src/providers/integrations/kiro/kiro-stream.ts +13 -0
  371. package/src/providers/integrations/kiro/kiro.ts +80 -68
  372. package/src/providers/integrations/muse/muse-oauth.ts +0 -1
  373. package/src/providers/integrations/muse/muse-quota.ts +0 -4
  374. package/src/providers/integrations/opencode/fingerprint.ts +7 -5
  375. package/src/providers/integrations/opencode/provider.ts +17 -24
  376. package/src/providers/integrations/qoder/qoder-catalog.ts +94 -11
  377. package/src/providers/integrations/qoder/qoder-oauth.ts +165 -25
  378. package/src/providers/integrations/qoder/qoder-quota.ts +48 -6
  379. package/src/providers/integrations/qoder/qoder-realm.ts +10 -31
  380. package/src/providers/integrations/qoder/qoder-sse.ts +159 -0
  381. package/src/providers/integrations/qoder/qoder-umid.ts +21 -25
  382. package/src/providers/integrations/qoder/qoder-virtual.ts +12 -0
  383. package/src/providers/integrations/qoder/qoder.ts +422 -71
  384. package/src/providers/integrations/shared/connect.ts +0 -54
  385. package/src/providers/integrations/shared/hash.ts +15 -0
  386. package/src/providers/integrations/shared/session-cache.ts +136 -0
  387. package/src/providers/integrations/trae/trae-cache.ts +7 -87
  388. package/src/providers/integrations/trae/trae-catalog.ts +1 -6
  389. package/src/providers/integrations/trae/trae-protocol.ts +17 -8
  390. package/src/providers/integrations/trae/trae-realm.ts +0 -7
  391. package/src/providers/integrations/trae/trae.ts +51 -8
  392. package/src/providers/integrations/xai/xai-oauth.ts +5 -5
  393. package/src/providers/integrations/zcode-start-plan/gifts.ts +265 -0
  394. package/src/providers/integrations/zcode-start-plan/oauth.ts +154 -0
  395. package/src/providers/integrations/zcode-start-plan/provider.ts +131 -0
  396. package/src/providers/integrations/zcode-start-plan/quota.ts +152 -0
  397. package/src/providers/integrations/zcode-start-plan/zcode_prompt.json +10 -0
  398. package/src/providers/operations/account-error-classification.ts +198 -133
  399. package/src/providers/operations/account-health-service.ts +31 -39
  400. package/src/providers/operations/account-reset-service.ts +58 -1
  401. package/src/providers/operations/byok-wire-profile.ts +19 -15
  402. package/src/providers/operations/cli-platform.ts +31 -5
  403. package/src/providers/operations/client-version-resolver.ts +4 -6
  404. package/src/providers/operations/client-versions.ts +10 -7
  405. package/src/providers/operations/provider-catalog-seeder.ts +2 -0
  406. package/src/providers/operations/provider-catalog-service.ts +18 -2
  407. package/src/providers/operations/provider-credential-service.ts +19 -2
  408. package/src/providers/provider-contracts.ts +18 -7
  409. package/src/providers/provider-metadata.ts +17 -2
  410. package/src/providers/provider-model-definition.ts +22 -1
  411. package/src/providers/provider-probe.ts +4 -2
  412. package/src/providers/provider-usage.ts +50 -19
  413. package/src/providers/quota/cache.ts +29 -0
  414. package/src/providers/quota/quota-contracts.ts +4 -0
  415. package/src/providers/quota/refresh.ts +27 -4
  416. package/src/providers/quota/snapshot.ts +94 -0
  417. package/src/providers/search/search-provider.ts +1 -3
  418. package/src/providers/search/search-providers.ts +2 -1
  419. package/src/security/abuse.ts +4 -4
  420. package/src/security/admission/service.ts +13 -6
  421. package/src/security/admission/ttl.ts +61 -61
  422. package/src/security/api-key-auth.ts +17 -3
  423. package/src/security/client-router-fingerprint.ts +4 -4
  424. package/src/security/credential-codec.ts +3 -3
  425. package/src/security/csrf.ts +2 -2
  426. package/src/security/ip-boundary.ts +16 -189
  427. package/src/security/model-access-rule.ts +1 -1
  428. package/src/security/outbound-headers.ts +18 -0
  429. package/src/shared/invoke-guard.ts +47 -0
  430. package/src/transport/contracts.ts +13 -4
  431. package/src/transport/dispatch/attempt-finalize.ts +70 -22
  432. package/src/transport/dispatch/attempt-loop.ts +22 -0
  433. package/src/transport/dispatch/capture-policy.ts +34 -37
  434. package/src/transport/dispatch/empty-stream.ts +23 -20
  435. package/src/transport/dispatch/generation.ts +25 -45
  436. package/src/transport/dispatch/native-shared.ts +127 -0
  437. package/src/transport/dispatch/proxy-request.ts +3 -1
  438. package/src/transport/dispatch/responses-compact.ts +25 -46
  439. package/src/transport/dispatch/streaming/encoder.ts +18 -72
  440. package/src/transport/dispatch/streaming/finalize.ts +2 -2
  441. package/src/transport/dispatch/streaming/stream.ts +7 -3
  442. package/src/transport/dispatch/streaming/thinking-loop.ts +1 -1
  443. package/src/transport/dispatch/systemone.ts +20 -48
  444. package/src/transport/dispatch/token-count.ts +41 -38
  445. package/src/transport/dispatch/websearch.ts +19 -45
  446. package/src/transport/drain-endpoint.ts +1 -1
  447. package/src/transport/failure-policy.ts +21 -1
  448. package/src/transport/guards/auth-guard.ts +0 -6
  449. package/src/transport/guards/body-policy.ts +3 -3
  450. package/src/transport/guards/error-lifecycle.ts +4 -0
  451. package/src/transport/guards/headroom-guard.ts +1 -1
  452. package/src/transport/guards/pipeline.ts +1 -1
  453. package/src/transport/guards/request-context.ts +12 -17
  454. package/src/transport/request/preparer.ts +286 -7
  455. package/src/transport/request/rtk/compress-request.ts +3 -3
  456. package/src/transport/request/rtk/rtk-constants.ts +2 -2
  457. package/src/transport/request/rtk/rtk-filters.ts +8 -8
  458. package/src/transport/request/state.ts +7 -13
  459. package/src/transport/request/system-prompt-injection.ts +74 -11
  460. package/src/transport/request/tenant-preferences.ts +22 -0
  461. package/src/transport/routing/candidate-index.ts +2 -2
  462. package/src/transport/routing/round-robin.ts +1 -1
  463. package/src/transport/routing/route-catalog.ts +17 -18
  464. package/src/transport/routing/route-model.ts +126 -33
  465. package/src/transport/routing/router.ts +159 -37
  466. package/src/transport/shutdown-notice.ts +2 -2
  467. package/src/transport/surface/adapters.ts +3 -3
  468. package/src/transport/surface/chat/encode.ts +7 -7
  469. package/src/transport/surface/chat/parse.ts +7 -7
  470. package/src/transport/surface/completion.ts +2 -2
  471. package/src/transport/surface/content-parts.ts +1 -1
  472. package/src/transport/surface/dialects.ts +2 -2
  473. package/src/transport/surface/messages/parse.ts +3 -3
  474. package/src/transport/surface/reasoning-summary.ts +9 -14
  475. package/src/transport/surface/responses/adapter.ts +1 -1
  476. package/src/transport/surface/responses/errors.ts +0 -9
  477. package/src/transport/vision-handoff-cache.ts +58 -0
  478. package/src/transport/vision-handoff.ts +431 -0
  479. package/src/workers/account-identity-sweep.ts +2 -2
  480. package/src/workers/buddy-growth-tasks.ts +392 -346
  481. package/src/workers/daily-checkin.ts +102 -49
  482. package/src/workers/maintenance-lease.ts +2 -35
  483. package/src/workers/oauth-refresh-sweep.ts +8 -1
  484. package/src/workers/quota-refresh-sweep.ts +120 -82
  485. package/src/workers/scheduler.ts +67 -4
  486. package/src/workers/sweep-runner.ts +24 -5
  487. package/src/workers/zcode-gift-claim.ts +108 -0
  488. package/dist/dashboard/assets/AccountCooldown-Bjo92Qat.js +0 -1
  489. package/dist/dashboard/assets/AccountCooldown-Bjo92Qat.js.gz +0 -0
  490. package/dist/dashboard/assets/BannedPage-ChG5svFb.js +0 -1
  491. package/dist/dashboard/assets/BannedPage-ChG5svFb.js.gz +0 -0
  492. package/dist/dashboard/assets/CliToolDetailPage-BIcWQun7.js +0 -1
  493. package/dist/dashboard/assets/CliToolDetailPage-BIcWQun7.js.gz +0 -0
  494. package/dist/dashboard/assets/CliToolsPage-Bf2VelDe.js +0 -1
  495. package/dist/dashboard/assets/CliToolsPage-Bf2VelDe.js.gz +0 -0
  496. package/dist/dashboard/assets/CombosPage-DCgm5g6g.js +0 -1
  497. package/dist/dashboard/assets/CombosPage-DCgm5g6g.js.gz +0 -0
  498. package/dist/dashboard/assets/ConsoleLogPage-a8mD0QF1.js +0 -1
  499. package/dist/dashboard/assets/ConsoleLogPage-a8mD0QF1.js.gz +0 -0
  500. package/dist/dashboard/assets/CustomizationPage-ggeMSjte.js +0 -1
  501. package/dist/dashboard/assets/CustomizationPage-ggeMSjte.js.gz +0 -0
  502. package/dist/dashboard/assets/HealthEventsModal-DzSWzE_j.js +0 -1
  503. package/dist/dashboard/assets/HealthEventsModal-DzSWzE_j.js.gz +0 -0
  504. package/dist/dashboard/assets/LoginPage-U6AKaJcF.js +0 -1
  505. package/dist/dashboard/assets/LoginPage-U6AKaJcF.js.gz +0 -0
  506. package/dist/dashboard/assets/ModelPicker-BMS2t8BA.js.gz +0 -0
  507. package/dist/dashboard/assets/OverviewPage-D2D4IiHl.js +0 -1
  508. package/dist/dashboard/assets/OverviewPage-D2D4IiHl.js.gz +0 -0
  509. package/dist/dashboard/assets/ProviderDetailPage-CrNf7j_X.js +0 -5
  510. package/dist/dashboard/assets/ProviderDetailPage-CrNf7j_X.js.gz +0 -0
  511. package/dist/dashboard/assets/ProviderIcon-FB6yS4v1.js +0 -1
  512. package/dist/dashboard/assets/ProviderIcon-FB6yS4v1.js.gz +0 -0
  513. package/dist/dashboard/assets/ProvidersPage-BSDbquc1.js +0 -1
  514. package/dist/dashboard/assets/ProvidersPage-BSDbquc1.js.gz +0 -0
  515. package/dist/dashboard/assets/ProxyPage-lsrx7NYg.js.gz +0 -0
  516. package/dist/dashboard/assets/QuotaPage-L0skzML8.js +0 -1
  517. package/dist/dashboard/assets/QuotaPage-L0skzML8.js.gz +0 -0
  518. package/dist/dashboard/assets/SettingsPage-CmAbjRHW.js +0 -1
  519. package/dist/dashboard/assets/SettingsPage-CmAbjRHW.js.gz +0 -0
  520. package/dist/dashboard/assets/SetupPage-f0DE1h0-.js +0 -1
  521. package/dist/dashboard/assets/SetupPage-f0DE1h0-.js.gz +0 -0
  522. package/dist/dashboard/assets/SortableList-ClzpwyET.js +0 -1
  523. package/dist/dashboard/assets/SortableList-ClzpwyET.js.gz +0 -0
  524. package/dist/dashboard/assets/StudioPage-Cq5tW7hW.js.gz +0 -0
  525. package/dist/dashboard/assets/ToolIcon-K1ckcDKg.js +0 -1
  526. package/dist/dashboard/assets/ToolIcon-K1ckcDKg.js.gz +0 -0
  527. package/dist/dashboard/assets/UsagePage-Dfc9rWQn.js +0 -56
  528. package/dist/dashboard/assets/UsagePage-Dfc9rWQn.js.gz +0 -0
  529. package/dist/dashboard/assets/api-keys-C38iXoO4.js +0 -1
  530. package/dist/dashboard/assets/api-keys-C38iXoO4.js.gz +0 -0
  531. package/dist/dashboard/assets/app-BayTTy01.js +0 -9
  532. package/dist/dashboard/assets/app-BayTTy01.js.gz +0 -0
  533. package/dist/dashboard/assets/app-a159k0ub.css +0 -2
  534. package/dist/dashboard/assets/app-a159k0ub.css.gz +0 -0
  535. package/dist/dashboard/assets/assert-DbRA8EG2.js +0 -15
  536. package/dist/dashboard/assets/assert-DbRA8EG2.js.gz +0 -0
  537. package/dist/dashboard/assets/badge-D9CS6dZk.js.gz +0 -0
  538. package/dist/dashboard/assets/banner-DympBNq4.js.gz +0 -0
  539. package/dist/dashboard/assets/card-ibRWU_uT.js.gz +0 -0
  540. package/dist/dashboard/assets/cli-tools-63qBGaRw.js +0 -1
  541. package/dist/dashboard/assets/cli-tools-63qBGaRw.js.gz +0 -0
  542. package/dist/dashboard/assets/clipboard-button-DJm-9pz7.js.gz +0 -0
  543. package/dist/dashboard/assets/dialog-8fiE6tec.js +0 -1
  544. package/dist/dashboard/assets/dialog-8fiE6tec.js.gz +0 -0
  545. package/dist/dashboard/assets/drawer-CqGYeOrd.js.gz +0 -0
  546. package/dist/dashboard/assets/entry-BSAsYsmV.js +0 -1
  547. package/dist/dashboard/assets/entry-BSAsYsmV.js.gz +0 -0
  548. package/dist/dashboard/assets/entry-CiIQULZD.js +0 -4
  549. package/dist/dashboard/assets/entry-CiIQULZD.js.gz +0 -0
  550. package/dist/dashboard/assets/entry-Daz0lJ-W.js +0 -1
  551. package/dist/dashboard/assets/entry-Daz0lJ-W.js.gz +0 -0
  552. package/dist/dashboard/assets/formatters-D-MTJxMr.js.gz +0 -0
  553. package/dist/dashboard/assets/github-badge-CPK47xQx.js +0 -1
  554. package/dist/dashboard/assets/github-badge-CPK47xQx.js.gz +0 -0
  555. package/dist/dashboard/assets/index-DBNPOfhV.js.gz +0 -0
  556. package/dist/dashboard/assets/inline-BT0zntEr.js.gz +0 -0
  557. package/dist/dashboard/assets/input-Da2ZViWg.js.gz +0 -0
  558. package/dist/dashboard/assets/layout-BXNeLw6M.js.gz +0 -0
  559. package/dist/dashboard/assets/list-row-BycOrrIV.js.gz +0 -0
  560. package/dist/dashboard/assets/live-DCQNZpF7.js +0 -1
  561. package/dist/dashboard/assets/live-DCQNZpF7.js.gz +0 -0
  562. package/dist/dashboard/assets/mutation-Rr0TIvcj.js.gz +0 -0
  563. package/dist/dashboard/assets/network-9Yj2ZyLo.js +0 -1
  564. package/dist/dashboard/assets/network-9Yj2ZyLo.js.gz +0 -0
  565. package/dist/dashboard/assets/numbers-CEagj6ZZ.js +0 -2
  566. package/dist/dashboard/assets/numbers-CEagj6ZZ.js.gz +0 -0
  567. package/dist/dashboard/assets/providers/zai-coding-plan.svg +0 -6
  568. package/dist/dashboard/assets/providers/zai-coding-plan.svg.gz +0 -0
  569. package/dist/dashboard/assets/quota-Caw7wqci.js +0 -1
  570. package/dist/dashboard/assets/quota-Caw7wqci.js.gz +0 -0
  571. package/dist/dashboard/assets/routing-CO9zCUus.js +0 -1
  572. package/dist/dashboard/assets/routing-CO9zCUus.js.gz +0 -0
  573. package/dist/dashboard/assets/select-BIYIBkMU.js.gz +0 -0
  574. package/dist/dashboard/assets/settings-jS3-PNSA.js +0 -1
  575. package/dist/dashboard/assets/settings-jS3-PNSA.js.gz +0 -0
  576. package/dist/dashboard/assets/stack-DfgnFDWY.js.gz +0 -0
  577. package/dist/dashboard/assets/state-CkfdYWel.js.gz +0 -0
  578. package/dist/dashboard/assets/stream-retry-5QnVuSSo.js +0 -1
  579. package/dist/dashboard/assets/stream-retry-5QnVuSSo.js.gz +0 -0
  580. package/dist/dashboard/assets/switch-SYm237HG.js.gz +0 -0
  581. package/dist/dashboard/assets/useMutation-BQqCRNIi.js.gz +0 -0
  582. package/src/console/cli-tools/injectors/cline.ts +0 -110
  583. package/src/console/cli-tools/injectors/copilot.ts +0 -108
  584. package/src/console/cli-tools/injectors/cowork.ts +0 -64
  585. package/src/console/cli-tools/injectors/deepseek-tui.ts +0 -85
  586. package/src/console/cli-tools/injectors/droid.ts +0 -96
  587. package/src/console/cli-tools/injectors/grok-build.ts +0 -70
  588. package/src/console/cli-tools/injectors/hermes.ts +0 -101
  589. package/src/console/cli-tools/injectors/jcode.ts +0 -115
  590. package/src/console/cli-tools/injectors/openclaw.ts +0 -161
  591. package/src/network/response-headers.ts +0 -39
  592. package/src/protocol/response/dsml.ts +0 -38
  593. package/src/providers/integrations/xiaomi-mimo/think-stream.ts +0 -229
  594. /package/dist/dashboard/assets/brand/{og_bansos.webp → og_bansos_initial.webp} +0 -0
  595. /package/dist/dashboard/assets/brand/{og_image.webp → og_image_initial.webp} +0 -0
@@ -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(
@@ -303,17 +303,6 @@ export function buildHeartbeat(): Uint8Array {
303
303
  return encodeMessage("AgentClientMessage", { client_heartbeat: {} });
304
304
  }
305
305
 
306
- /**
307
- * `ACM#5 exec_client_control_message.heartbeat{id}`: Cursor CLI sends one every
308
- * few seconds for each exec it is still running. A zero id is left out, as
309
- * proto3 does for a default scalar.
310
- */
311
- export function buildExecHeartbeat(execId: number): Uint8Array {
312
- return encodeMessage("AgentClientMessage", {
313
- exec_client_control_message: { heartbeat: execId === 0 ? {} : { id: execId } },
314
- });
315
- }
316
-
317
306
  /** `ACM#5 exec_client_control_message.stream_close{id}` after an exec result. */
318
307
  export function buildExecStreamClose(execId: number, execIdString?: string): Uint8Array {
319
308
  return encodeMessage("AgentClientMessage", {
@@ -424,82 +413,6 @@ export function buildMcpResult(input: {
424
413
  });
425
414
  }
426
415
 
427
- /**
428
- * `ACM#2 exec_client_message{shell_result|write_result|read_result}` for a host
429
- * exec the canonical client executed locally.
430
- *
431
- * Cursor's executor replies to its own host requests with the variant's typed
432
- * result frame (the generic `mcp_result` leaves the server unmatched on a frame
433
- * it did rank), and after every exec result it sends `stream_close` — without
434
- * it the cloud waits forever on shell execs. `is_error` keeps the failure as
435
- * the variant's error/failure case rather than a crafted success.
436
- */
437
- export type CursorHostExecResultVariant = "shell" | "write" | "read";
438
-
439
- export function buildHostExecResult(input: {
440
- readonly execId: number;
441
- readonly execIdString?: string;
442
- readonly variant: CursorHostExecResultVariant;
443
- readonly text: string;
444
- readonly isError: boolean;
445
- readonly host: {
446
- readonly command?: string;
447
- readonly workingDirectory?: string;
448
- readonly path?: string;
449
- readonly lines?: number;
450
- readonly fileSize?: number;
451
- };
452
- }): Uint8Array[] {
453
- const id = { id: input.execId, exec_id: input.execIdString ?? "" };
454
- const variant = input.variant;
455
- const text = input.text;
456
- let result: Record<string, unknown>;
457
- if (variant === "shell") {
458
- result = {
459
- shell_result: input.isError
460
- ? { failure: { command: input.host.command ?? "", exit_code: 1, stderr: text } }
461
- : {
462
- success: {
463
- command: input.host.command ?? "",
464
- exit_code: 0,
465
- stdout: text,
466
- ...(input.host.workingDirectory === undefined ? {} : { working_directory: input.host.workingDirectory }),
467
- },
468
- },
469
- };
470
- } else if (variant === "write") {
471
- result = {
472
- write_result: input.isError
473
- ? { error: { path: input.host.path ?? "", error: text } }
474
- : {
475
- success: {
476
- path: input.host.path ?? "",
477
- lines_created: input.host.lines ?? 0,
478
- file_size: input.host.fileSize ?? 0,
479
- },
480
- },
481
- };
482
- } else {
483
- result = {
484
- read_result: input.isError
485
- ? { error: { path: input.host.path ?? "", error: text } }
486
- : {
487
- success: {
488
- path: input.host.path ?? "",
489
- content: text,
490
- total_lines: text.length > 0 ? text.split("\n").length : 0,
491
- },
492
- },
493
- };
494
- }
495
- return [
496
- encodeMessage("AgentClientMessage", { exec_client_message: { ...id, ...result } }),
497
- // The real CLI pairs every typed exec result with stream_close — the cloud
498
- // waits on it for shell execs specifically.
499
- buildExecStreamClose(input.execId, input.execIdString),
500
- ];
501
- }
502
-
503
416
  /**
504
417
  * `ACM#2 exec_client_message{mcp_result}` rejecting a tool the client never
505
418
  * declared.
@@ -5,7 +5,6 @@
5
5
  // protocol was verified against: they are declared once so the auth flow, the
6
6
  // unary catalog calls, and the Run stream cannot drift apart on host, path, or
7
7
  // client id.
8
- import type { WireFamily } from "../../../protocol/canonical-model";
9
8
 
10
9
  /** Canonical bundled provider id. */
11
10
  export const CURSOR_PROVIDER_ID = "cursor";
@@ -72,9 +71,6 @@ export const SESSION_REFRESH_TIMEOUT_MS = 20_000;
72
71
  */
73
72
  export const RUN_IDLE_TTL_MS = 10 * 60_000;
74
73
 
75
- /** Chat wire is the declared default; the adapter owns the native protocol. */
76
- export const CURSOR_WIRE_FAMILY: WireFamily = "chat";
77
-
78
74
  /**
79
75
  * Any HTTPS subdomain of `cursor.sh` is an acceptable Run host: the region
80
76
  * Cursor routes an account to varies (`agentn.*`, `agent.*`, `api5`, …).
@@ -62,6 +62,7 @@ import { defaultParameterValues, fetchCursorModels } from "./cursor-catalog";
62
62
  import type { CursorModelInfo } from "./cursor-catalog";
63
63
  import { decodeValueToJson, decodeStructEntriesToJson } from "./protocol/struct";
64
64
  import { cursorVariantForExecArgs } from "./protocol/tool-canonical";
65
+ import { fnv1a32 } from "../shared/hash";
65
66
 
66
67
  /**
67
68
  * Client-heartbeat cadence for every Cursor Run.
@@ -81,8 +82,38 @@ const CURSOR_HEARTBEAT_INTERVAL_MS = 5_000;
81
82
  * every dispatch.
82
83
  */
83
84
  const CURSOR_ROSTER_TTL_MS = 300_000;
84
- /** credential → { roster, fetchedAt } for the parameter lookup. */
85
+ /**
86
+ * credential → { roster, fetchedAt } for the parameter lookup.
87
+ *
88
+ * Keyed by an FNV-1a digest of the credential rather than the raw token so a
89
+ * long-lived map does not hold live credentials as keys. Bounded by
90
+ * `ROSTER_CACHE_MAX` with oldest-first eviction: the TTL is only checked on
91
+ * read, so without a cap rotated tokens would grow the map without bound.
92
+ */
93
+ const ROSTER_CACHE_MAX = 1024;
85
94
  const rosterCache = new Map<string, { roster: readonly CursorModelInfo[]; fetchedAt: number }>();
95
+
96
+ function rosterCacheSet(credential: string, roster: readonly CursorModelInfo[]): void {
97
+ const key = String(fnv1a32(credential));
98
+ rosterCache.delete(key); // re-set moves the entry to newest position
99
+ rosterCache.set(key, { roster, fetchedAt: Date.now() });
100
+ while (rosterCache.size > ROSTER_CACHE_MAX) {
101
+ const oldest = rosterCache.keys().next().value;
102
+ if (oldest === undefined) break;
103
+ rosterCache.delete(oldest);
104
+ }
105
+ }
106
+
107
+ /**
108
+ * Test-only access to the roster cache (cap/eviction/TTL behavior is covered
109
+ * by `test/providers/cursor-roster-cache.test.ts` without a full dispatch).
110
+ */
111
+ export const rosterCacheForTests = {
112
+ size: (): number => rosterCache.size,
113
+ set: rosterCacheSet,
114
+ get: (credential: string): { roster: readonly CursorModelInfo[]; fetchedAt: number } | undefined =>
115
+ rosterCache.get(String(fnv1a32(credential))),
116
+ };
86
117
  /** Client-heartbeat cadence the Cursor edge expects (donor `heartbeatMs`). */
87
118
  const CURSOR_HEARTBEAT_INTERVAL_OVERRIDE = (() => {
88
119
  const raw = process.env.CARTETHYIA_CURSOR_HEARTBEAT_MS?.trim();
@@ -846,7 +877,8 @@ class CursorAdapter implements ProviderAdapter {
846
877
  credential: string,
847
878
  context: ProviderDispatchContext,
848
879
  ): Promise<readonly CursorModelInfo[]> {
849
- const cached = rosterCache.get(credential);
880
+ const key = String(fnv1a32(credential));
881
+ const cached = rosterCache.get(key);
850
882
  if (cached !== undefined && Date.now() - cached.fetchedAt < CURSOR_ROSTER_TTL_MS) {
851
883
  return cached.roster;
852
884
  }
@@ -858,7 +890,7 @@ class CursorAdapter implements ProviderAdapter {
858
890
  : { fetcher: context.outbound_fetch as unknown as typeof fetch }),
859
891
  signal: context.abort_signal,
860
892
  });
861
- rosterCache.set(credential, { roster, fetchedAt: Date.now() });
893
+ rosterCacheSet(credential, roster);
862
894
  return roster;
863
895
  } catch {
864
896
  return cached?.roster ?? [];
@@ -71,59 +71,3 @@ export function* streamFrames(buffer: Uint8Array): Generator<Frame, void, void>
71
71
  offset = start + len
72
72
  }
73
73
  }
74
-
75
- export async function* asyncStreamFrames(
76
- stream: ReadableStream<Uint8Array>,
77
- ): AsyncGenerator<Frame, void, void> {
78
- const reader = stream.getReader()
79
- const chunks: Uint8Array[] = []
80
- let pendingLength = 0
81
-
82
- try {
83
- while (true) {
84
- const { done, value } = await reader.read()
85
- if (done && chunks.length === 0) return
86
- if (value) {
87
- chunks.push(value)
88
- pendingLength += value.length
89
- }
90
-
91
- // Try to parse frames from accumulated data
92
- const merged = mergeChunks(chunks)
93
- const frames = Array.from(streamFrames(merged))
94
- const consumed = frames.reduce((sum, f) => sum + 5 + f.payload.length, 0)
95
-
96
- if (frames.length > 0) {
97
- for (const frame of frames) {
98
- yield frame
99
- }
100
- // Keep any trailing bytes for next iteration
101
- const remaining = merged.subarray(consumed)
102
- chunks.length = 0
103
- if (remaining.length > 0) {
104
- chunks.push(remaining)
105
- pendingLength = remaining.length
106
- } else {
107
- pendingLength = 0
108
- }
109
- }
110
-
111
- if (done) return
112
- }
113
- } finally {
114
- reader.releaseLock()
115
- }
116
- }
117
-
118
- function mergeChunks(chunks: Uint8Array[]): Uint8Array {
119
- const first = chunks[0]
120
- if (chunks.length === 1 && first !== undefined) return first
121
- const total = chunks.reduce((s, c) => s + c.length, 0)
122
- const merged = new Uint8Array(total)
123
- let offset = 0
124
- for (const c of chunks) {
125
- merged.set(c, offset)
126
- offset += c.length
127
- }
128
- return merged
129
- }
@@ -2282,99 +2282,3 @@ export function decodeMessage<T = Record<string, unknown>>(
2282
2282
  const decoded = type.decode(data)
2283
2283
  return type.toObject(decoded, { defaults: true, json: true, longs: Number }) as T
2284
2284
  }
2285
-
2286
- /** Decode without inventing absent proto defaults (used for byte-stable snapshots). */
2287
- export function decodeMessageSparse<T = Record<string, unknown>>(
2288
- typeName: string,
2289
- data: Uint8Array,
2290
- ): T {
2291
- const root = getMessageTypes()
2292
- const type = root.lookupType(typeName)
2293
- const decoded = type.decode(data)
2294
- return type.toObject(decoded, { json: true, longs: Number }) as T
2295
- }
2296
-
2297
- type RawField = {
2298
- field: number
2299
- wireType: number
2300
- varint?: string
2301
- fixed?: number | string
2302
- bytes?: Uint8Array
2303
- }
2304
-
2305
- /** Schema-free walk of one message's top-level wire fields (no nested descent). */
2306
- function readRawFields(buf: Uint8Array): RawField[] {
2307
- const reader = protobuf.Reader.create(buf)
2308
- const fields: RawField[] = []
2309
- while (reader.pos < reader.len) {
2310
- const tag = reader.uint32()
2311
- const field = tag >>> 3
2312
- const wireType = tag & 7
2313
- switch (wireType) {
2314
- case 0:
2315
- fields.push({ field, wireType, varint: reader.uint64().toString() })
2316
- break
2317
- case 1:
2318
- fields.push({ field, wireType, fixed: reader.fixed64().toString() })
2319
- break
2320
- case 2:
2321
- fields.push({ field, wireType, bytes: reader.bytes() })
2322
- break
2323
- case 5:
2324
- fields.push({ field, wireType, fixed: reader.fixed32() })
2325
- break
2326
- default:
2327
- // Groups (3/4) aren't used on this wire; bail rather than desync the reader.
2328
- return fields
2329
- }
2330
- }
2331
- return fields
2332
- }
2333
-
2334
- /**
2335
- * Diagnostic-only: walk AgentServerMessage.interaction_update(1).turn_ended(14)
2336
- * without going through our hand-written TurnEnded schema, so a `cache_write`/
2337
- * `reasoning_tokens` question can be checked against the raw wire instead of
2338
- * guessed. Confirmed field ids 1-5 (input/output/cache_read/cache_write/
2339
- * reasoning) decode correctly. Live captures routinely include `f4` explicitly
2340
- * as `0` while `f3` (cache_read) and `f5` (reasoning) are non-zero — Cursor's
2341
- * agent TurnEnded populates the field but does not report write counts here.
2342
- */
2343
- export function debugWalkTurnEnded(payload: Uint8Array): string {
2344
- try {
2345
- const top = readRawFields(payload)
2346
- const iuField = top.find((f) => f.field === 1 && f.wireType === 2)
2347
- if (!iuField?.bytes) return "(no interaction_update field)"
2348
- const iu = readRawFields(iuField.bytes)
2349
- const teField = iu.find((f) => f.field === 14 && f.wireType === 2)
2350
- if (!teField?.bytes) return "(no turn_ended field in interaction_update)"
2351
- const te = readRawFields(teField.bytes)
2352
- return te
2353
- .map(
2354
- (f) =>
2355
- `f${f.field}:wt${f.wireType}=${f.varint ?? f.fixed ?? (f.bytes ? `bytes[${f.bytes.length}]` : "?")}`,
2356
- )
2357
- .join(" ")
2358
- } catch (e) {
2359
- return `(walk failed: ${(e as Error).message})`
2360
- }
2361
- }
2362
-
2363
- /**
2364
- * Decode a protobuf sub-message from a frame payload that wraps it in
2365
- * a top-level field key + length varint. Skips the outer wrapper and
2366
- * decodes the inner body as `typeName`.
2367
- */
2368
- export function decodeWrappedMessage<T = Record<string, unknown>>(
2369
- typeName: string,
2370
- data: Uint8Array,
2371
- ): T {
2372
- let offset = 1 // skip field key
2373
- // skip varint length
2374
- while (offset < data.length) {
2375
- const b = data[offset] ?? 0
2376
- offset++
2377
- if (!(b & 0x80)) break
2378
- }
2379
- return decodeMessage<T>(typeName, data.subarray(offset))
2380
- }