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
@@ -1,6 +1,5 @@
1
1
  import type { ConsoleAccessResolver } from "../auth/access";
2
2
  import type { AuditRecorder } from "../auth/audit-recorder";
3
- import { markClineApiKeyCredential } from "../../providers/integrations/cline/cline-quota";
4
3
  import { type QuotaRefreshDeps } from "../../providers/quota/refresh";
5
4
  import type { AccountTestEgressDeps } from "./account-test-egress";
6
5
 
@@ -31,8 +30,16 @@ export type AccountStatus = (typeof ACCOUNT_STATUSES)[number];
31
30
  * omitted it, so a working account was cooled with `quota_fetch_failed` every
32
31
  * minute while refreshing fine from the console.
33
32
  */
34
- export function markProviderApiKeyCredential(providerId: string, credential: string): string {
35
- return providerId === "cline" ? markClineApiKeyCredential(credential) : credential;
33
+ export async function markProviderApiKeyCredential(providerId: string, credential: string): Promise<string> {
34
+ if (providerId !== "cline") return credential;
35
+ // dynamic-import: B-02 lazy graph — cline-only marker, kept off the boot graph.
36
+ // The cline-quota graph is loaded lazily: only `cline` accounts ever need
37
+ // the marker, so a deployment without cline must not pull it into the boot
38
+ // graph (B-02).
39
+ const { markClineApiKeyCredential } = await import(
40
+ "../../providers/integrations/cline/cline-quota"
41
+ );
42
+ return markClineApiKeyCredential(credential);
36
43
  }
37
44
 
38
45
  /**
@@ -4,17 +4,22 @@ import type { Elysia } from "elysia";
4
4
  import { and, eq, inArray } from "drizzle-orm";
5
5
  import { providerAccounts, providers } from "../../persistence/schema";
6
6
  import { providerDisplayName } from "../../providers/provider-metadata";
7
- import { invalidateCredentialCache } from "../../providers/operations/provider-credential-service";
7
+ import {
8
+ invalidateCredentialCache,
9
+ invalidateCredentialCaches,
10
+ } from "../../providers/operations/provider-credential-service";
8
11
  import { requireTenantScope } from "../shared/errors";
9
12
  import { BUDDY_PROVIDER_IDS } from "../../providers/provider-metadata";
10
- import { readBuddyUsageSnapshots } from "../../providers/integrations/buddy/buddy-usage-store";
11
13
  import { GatewayError } from "../../protocol/gateway-error";
12
14
  import {
13
15
  GLOBAL_QUOTA_LENS,
14
16
  getCachedQuotaEntries,
15
17
  getCachedQuotaEntry,
16
18
  invalidateQuotaCache,
19
+ invalidateQuotaCaches,
20
+ invalidateQuotaCacheForAccount,
17
21
  } from "../../providers/quota/cache";
22
+ import { readQuotaSnapshot, readQuotaSnapshots } from "../../providers/quota/snapshot";
18
23
  import {
19
24
  QUOTA_REFRESH_TIMEOUT_MS,
20
25
  refreshAccountQuota,
@@ -47,10 +52,12 @@ import {
47
52
  DrizzleQoderNativeIdentityStore,
48
53
  type QoderNativeIdentityStore,
49
54
  } from "../../providers/integrations/qoder/qoder-native-identity-store";
50
- import { buddyAccountUid } from "../../providers/integrations/buddy/buddy-oauth-shared";
55
+ import { claimZcodeGifts, type ZcodeGiftResult } from "../../providers/integrations/zcode-start-plan/gifts";
56
+ import { ZCODE_START_PLAN_PROVIDER_ID } from "../../providers/integrations/zcode-start-plan/provider";
51
57
  import {
52
58
  supportsAccountReset,
53
59
  consumeAccountResetCredit,
60
+ getCachedResetCredits,
54
61
  listAccountResetCredits,
55
62
  } from "../../providers/operations/account-reset-service";
56
63
  import {
@@ -64,6 +71,14 @@ import {
64
71
  type AccountQuotaRoutesDeps,
65
72
  type AccountStatus,
66
73
  } from "./account-quota-shared";
74
+ import {
75
+ DAILY_GROWTH_LEG_TIMEOUT_MS,
76
+ enqueueDailyGrowthJob,
77
+ getDailyGrowthJob,
78
+ withLegBudget,
79
+ type DailyGrowthJobAccount,
80
+ type DailyGrowthJobDeps,
81
+ } from "./daily-growth-job";
67
82
 
68
83
  /** Daily check-in only exists on the buddy billing facade; everything else 400s with intent. */
69
84
  function supportsAccountCheckin(providerId: string): boolean {
@@ -133,10 +148,6 @@ async function runAccountCheckin(
133
148
  });
134
149
  }
135
150
 
136
- /** Bound on accounts one manual daily-growth batch may cover. */
137
- /** Concurrent accounts inside one manual daily-growth batch. */
138
- const DAILY_GROWTH_CONCURRENCY = 3;
139
-
140
151
  /**
141
152
  * One account's daily growth pass, report first then claim.
142
153
  *
@@ -175,6 +186,12 @@ async function runDailyGrowthForAccount(
175
186
  // fails closed instead of reporting under another account's identity. Qoder
176
187
  // has no activity-report leg — its daily grant is purely the check-in.
177
188
  const qoderAccount = isQoderCheckinProvider(String(providerId));
189
+ // dynamic-import: B-02 lazy graph — buddy uid decoder, only for buddy report legs.
190
+ // The buddy-oauth-shared graph is lazy-loaded: the uid decoder is only
191
+ // needed for buddy accounts' report leg (B-02).
192
+ const { buddyAccountUid } = await import(
193
+ "../../providers/integrations/buddy/buddy-oauth-shared"
194
+ );
178
195
  const userId = buddyAccountUid(credential);
179
196
  let report: { ok: boolean; message: string };
180
197
  if (qoderAccount) {
@@ -353,6 +370,13 @@ export function registerAccountQuotaTenantRoutes(
353
370
  getCachedQuotaEntries(access.tenantId, ownedIds, redis),
354
371
  getCachedQuotaEntries(GLOBAL_QUOTA_LENS, globalIds, redis),
355
372
  ]);
373
+ // A page open right after a restart finds the in-process cache empty.
374
+ // Fall back to the persisted snapshots (which survive a restart) so the
375
+ // page still renders the last known balance; the sweep refreshes it.
376
+ const snapshots = await readQuotaSnapshots(
377
+ db,
378
+ rows.filter((row) => !globalCache.has(row.id) && !ownedCache.has(row.id)).map((row) => row.id),
379
+ );
356
380
 
357
381
  const pending = pendingQuotaRefreshes();
358
382
  const providerIds = [...new Set(rows.map((row) => row.providerId))];
@@ -395,6 +419,12 @@ export function registerAccountQuotaTenantRoutes(
395
419
  // the upstream: the page renders every account at once, and re-hitting the
396
420
  // billing endpoint per account would make opening it scale with the
397
421
  // account count. One batch read for every buddy row.
422
+ // dynamic-import: B-02 lazy graph — buddy usage snapshots, only when the page renders buddy accounts.
423
+ // The buddy-usage-store graph is lazy-loaded: it is only needed when the
424
+ // page actually renders buddy accounts (B-02).
425
+ const { readBuddyUsageSnapshots } = await import(
426
+ "../../providers/integrations/buddy/buddy-usage-store"
427
+ );
398
428
  const usageByAccount = await readBuddyUsageSnapshots(
399
429
  db,
400
430
  rows.filter((row) => isBuddyProvider(row.providerId)).map((row) => row.id),
@@ -402,6 +432,9 @@ export function registerAccountQuotaTenantRoutes(
402
432
  const accounts = rows.map((row) => {
403
433
  const entry =
404
434
  row.tenantId === null ? globalCache.get(row.id) : ownedCache.get(row.id);
435
+ const snapshot = entry ?? snapshots.get(row.id);
436
+ const entryQuota = snapshot ? snapshot.quota : null;
437
+ const entryFetchedAt = snapshot ? snapshot.fetchedAt : null;
405
438
  // No background refresh is enqueued here: enqueuing one for every
406
439
  // account whose cached value had aged out means a single page open hits
407
440
  // every provider's quota endpoint at once — and because the page also
@@ -435,13 +468,13 @@ export function registerAccountQuotaTenantRoutes(
435
468
  name: row.label || row.id,
436
469
  credentialHint: row.credentialKind,
437
470
  active: row.status === "active",
438
- quota: entry
439
- ? toQuotaView(entry.quota, row, {
440
- fetchedAt: entry.fetchedAt,
441
- lastAttemptAt: entry.lastAttemptAt,
442
- refreshError: entry.refreshError,
443
- ...(isRefreshing ? { status: "refreshing" as const } : {}),
444
- })
471
+ quota: entryQuota
472
+ ? toQuotaView(entryQuota, row, {
473
+ fetchedAt: entryFetchedAt,
474
+ lastAttemptAt: entry?.lastAttemptAt ?? null,
475
+ refreshError: entry?.refreshError ?? null,
476
+ ...(isRefreshing ? { status: "refreshing" as const } : {}),
477
+ })
445
478
  : null,
446
479
  pending: isRefreshing,
447
480
  health: toQuotaAccountHealth(row),
@@ -503,13 +536,33 @@ export function registerAccountQuotaTenantRoutes(
503
536
  health: toQuotaAccountHealth(account),
504
537
  };
505
538
  }
539
+ // Cache is cold (e.g. right after a restart): render the last persisted
540
+ // snapshot instead of hitting upstream on every page open. The sweep
541
+ // refreshes it; a page open does not need to pay the upstream cost.
542
+ const snapshot = await readQuotaSnapshot(db, accountId);
543
+ if (snapshot) {
544
+ return {
545
+ accountId,
546
+ quota: toQuotaView(snapshot.quota, account, {
547
+ fetchedAt: snapshot.fetchedAt,
548
+ lastAttemptAt: snapshot.fetchedAt,
549
+ }),
550
+ health: toQuotaAccountHealth(account),
551
+ };
552
+ }
506
553
 
507
554
  // Cold read: this is the one path that legitimately waits on upstream, and
508
555
  // it shares the in-flight fetch with any queued background fill so a cold
509
556
  // page open never issues two requests for the same account.
510
557
  const outcome = await refreshAccountQuota(
511
558
  refreshDeps,
512
- { accountId, providerId: account.providerId, tenantId: account.tenantId, credentialKind: account.credentialKind },
559
+ {
560
+ accountId,
561
+ providerId: account.providerId,
562
+ tenantId: account.tenantId,
563
+ credentialKind: account.credentialKind,
564
+ authState: account.authState,
565
+ },
513
566
  signalFetch(timeoutSignal(request.signal, QUOTA_REFRESH_TIMEOUT_MS)),
514
567
  );
515
568
  const fresh = await getCachedQuotaEntry(access.tenantId, accountId, redis);
@@ -571,7 +624,16 @@ export function registerAccountQuotaTenantRoutes(
571
624
  try {
572
625
  outcome = await refreshAccountQuota(
573
626
  refreshDeps,
574
- { accountId, providerId: account.providerId, tenantId: account.tenantId, credentialKind: account.credentialKind },
627
+ {
628
+ accountId,
629
+ providerId: account.providerId,
630
+ tenantId: account.tenantId,
631
+ credentialKind: account.credentialKind,
632
+ // Kiro's usage surface is profile-scoped: without the account's auth
633
+ // state the collector cannot resolve a `profileArn`, and the GET is
634
+ // refused with `400 Invalid profileArn.`.
635
+ authState: account.authState,
636
+ },
575
637
  egress.fetch,
576
638
  );
577
639
  } finally {
@@ -616,6 +678,7 @@ export function registerAccountQuotaTenantRoutes(
616
678
  providerId: providerAccounts.providerId,
617
679
  tenantId: providerAccounts.tenantId,
618
680
  credentialKind: providerAccounts.credentialKind,
681
+ authState: providerAccounts.authState,
619
682
  })
620
683
  .from(providerAccounts)
621
684
  .where(
@@ -776,17 +839,60 @@ export function registerAccountQuotaTenantRoutes(
776
839
  });
777
840
  return { success: true };
778
841
  })
842
+ .post(
843
+ "/accounts/batch-delete",
844
+ {
845
+ body: t.Object({
846
+ accountIds: t.Array(t.String()),
847
+ }),
848
+ },
849
+ async ({ request, body }) => {
850
+ const access = requireTenantScope(accessResolver(request), "dashboard:write");
851
+ const accountIds = [...new Set(body.accountIds)];
852
+ if (accountIds.length === 0) {
853
+ return { ok: true, deleted: [], missing: [] };
854
+ }
855
+ // One DELETE statement for the whole batch, scoped to the tenant: a
856
+ // global/shared account (`tenant_id IS NULL`) is never matchable, so
857
+ // it lands in `missing` rather than being deleted by a tenant.
858
+ const deleted = await db
859
+ .delete(providerAccounts)
860
+ .where(
861
+ and(
862
+ inArray(providerAccounts.id, accountIds),
863
+ eq(providerAccounts.tenantId, access.tenantId),
864
+ ),
865
+ )
866
+ .returning({ id: providerAccounts.id });
867
+ const deletedIds = deleted.map((row) => row.id);
868
+ const deletedSet = new Set(deletedIds);
869
+ const missing = accountIds.filter((id) => !deletedSet.has(id));
870
+ // One credential-cache pass, one quota-cache pass, one snapshot
871
+ // invalidation, one audit row — never one per deleted account.
872
+ invalidateCredentialCaches(deletedIds);
873
+ await invalidateQuotaCaches(access.tenantId, deletedIds, redis);
874
+ await snapshotInvalidator?.invalidate();
875
+ await auditRecorder?.record({
876
+ access,
877
+ action: "provider_account.batch_deleted",
878
+ target: access.tenantId,
879
+ detail: { deleted: deletedIds, missing },
880
+ });
881
+ return { ok: true, deleted: deletedIds, missing };
882
+ },
883
+ )
779
884
  /**
780
- * Runs the daily growth pass over a provider's accounts in one request:
781
- * the growth-activity report first, then the check-in claim — the order the
885
+ * Enqueues a background daily-growth job over a provider's accounts: the
886
+ * growth-activity report first, then the check-in claim — the order the
782
887
  * sweep uses — with egress rotated per account the same way the worker
783
888
  * does, so a batch leaves from more than one address.
784
889
  *
785
- * A manual trigger is the operator asserting "try now", so it bypasses the
786
- * sweep's once-per-day ledger; the upstream stays idempotent either way.
787
- * Per-account outcomes are reported individually: a batch is not
788
- * all-or-nothing, and one account's closed check-in must not hide the
789
- * others' streaks.
890
+ * Running the whole batch in the request held it open for every account's
891
+ * upstream calls; the route now returns `{ jobId }` immediately and the
892
+ * work drains through a bounded background worker (see the job status
893
+ * route below). A manual trigger is the operator asserting "try now", so
894
+ * it bypasses the sweep's once-per-day ledger; the upstream stays
895
+ * idempotent either way.
790
896
  */
791
897
  .post(
792
898
  "/quota/daily-growth",
@@ -832,59 +938,66 @@ export function registerAccountQuotaTenantRoutes(
832
938
  };
833
939
  }
834
940
  // No size cap, for the same reason as the quota refresh: the work is
835
- // already drained through a bounded worker pool, so refusing the request
836
- // protected nothing and just forced the operator to batch by hand.
837
- // One egress rotator for the whole request, exactly as the sweep builds
838
- // it, so consecutive accounts leave from consecutive pools.
941
+ // already drained through a bounded worker pool behind the job, so
942
+ // refusing the request protected nothing and just forced the operator
943
+ // to batch by hand.
944
+ //
945
+ // One egress rotator for the whole job, exactly as the sweep builds it,
946
+ // so consecutive accounts leave from consecutive pools. The job carries
947
+ // the per-account run (report then claim) through the injected runner.
839
948
  const rotator =
840
949
  networkBindingFactory !== undefined
841
950
  ? dailyCheckinEgressForPass({ db, networkBindingFactory })
842
951
  : undefined;
952
+ // One leg budget for the whole job: every upstream fetch the per-account
953
+ // run issues carries it on top of the helpers' own per-request timeouts.
954
+ const legSignal = AbortSignal.timeout(DAILY_GROWTH_LEG_TIMEOUT_MS);
955
+ const rotatorWithBudget =
956
+ rotator === undefined
957
+ ? undefined
958
+ : async (accountId: string, tenantId?: string | null) => {
959
+ const base = await rotator(accountId, tenantId);
960
+ return withLegBudget(base, legSignal) as unknown as ValidatedFetch;
961
+ };
843
962
 
844
- const results: {
845
- readonly accountId: string;
846
- readonly label: string;
847
- readonly report: { readonly ok: boolean; readonly message: string };
848
- readonly checkin: { readonly ok: boolean; readonly message: string };
849
- }[] = [];
850
-
851
- for (let offset = 0; offset < accounts.length; offset += DAILY_GROWTH_CONCURRENCY) {
852
- const batch = accounts.slice(offset, offset + DAILY_GROWTH_CONCURRENCY);
853
- const settled = await Promise.all(
854
- batch.map(async (account) => {
855
- const outcome = await runDailyGrowthForAccount(
856
- providerId as ProviderId,
857
- account,
858
- refreshDeps,
859
- rotator,
860
- );
861
- return { account, outcome };
862
- }),
963
+ const runAccount = async (account: DailyGrowthJobAccount) => {
964
+ const outcome = await runDailyGrowthForAccount(
965
+ providerId as ProviderId,
966
+ account,
967
+ refreshDeps,
968
+ rotatorWithBudget,
863
969
  );
864
- for (const { account, outcome } of settled) {
865
- results.push({
866
- accountId: account.id,
867
- label: account.label || account.id,
868
- report: outcome.report,
869
- checkin: outcome.checkin,
870
- });
970
+ if (!outcome.checkin.ok) {
971
+ return { ok: false, error: outcome.checkin.message };
871
972
  }
872
- }
973
+ return { ok: true };
974
+ };
873
975
 
874
- // The ledger is shared with the sweep, so invalidate once at the end
875
- // rather than per account — the page re-reads "attempted today" from it.
876
- await snapshotInvalidator?.invalidate();
877
- const reportsOk = results.filter((entry) => entry.report.ok).length;
878
- const claimed = results.filter((entry) => entry.checkin.ok).length;
879
- return {
880
- ok: true,
881
- status: 200,
882
- providerId,
883
- data: results,
884
- message: `${results.length} account${results.length === 1 ? "" : "s"} · report ${reportsOk}/${results.length} · check-in ${claimed}/${results.length}`,
976
+ const jobDeps: DailyGrowthJobDeps = {
977
+ db,
978
+ redis,
979
+ runAccount,
980
+ ...(snapshotInvalidator ? { snapshotInvalidator } : {}),
885
981
  };
982
+ const jobId = enqueueDailyGrowthJob({
983
+ deps: jobDeps,
984
+ tenantId: access.tenantId,
985
+ providerId,
986
+ accounts,
987
+ });
988
+ set.status = 202;
989
+ return { jobId, status: 202 };
886
990
  },
887
991
  )
992
+ .get("/quota/daily-growth/status/:jobId", async ({ request, params, set }) => {
993
+ const access = requireTenantScope(accessResolver(request), "dashboard:read");
994
+ const job = getDailyGrowthJob(params.jobId, access.tenantId);
995
+ if (job === null) {
996
+ set.status = 404;
997
+ return { error: `Job ${params.jobId} not found`, code: "not_found" };
998
+ }
999
+ return job;
1000
+ })
888
1001
  .post("/accounts/:id/checkin", async ({ request, params, set }) => {
889
1002
  const access = requireTenantScope(accessResolver(request), "dashboard:write");
890
1003
  const rows = await db
@@ -954,6 +1067,75 @@ export function registerAccountQuotaTenantRoutes(
954
1067
  message: checkinStateMessage(result),
955
1068
  };
956
1069
  })
1070
+ // Gift grants are an explicit action, not a side effect of a quota read.
1071
+ .post("/accounts/:id/claim-gifts", async ({ request, params, set }) => {
1072
+ const access = requireTenantScope(accessResolver(request), "dashboard:write");
1073
+ const rows = await db
1074
+ .select()
1075
+ .from(providerAccounts)
1076
+ .where(
1077
+ and(eq(providerAccounts.id, params.id), eq(providerAccounts.tenantId, access.tenantId)),
1078
+ )
1079
+ .limit(1);
1080
+ const account = rows[0];
1081
+ if (!account) {
1082
+ set.status = 404;
1083
+ return { error: `Account ${params.id} not found`, code: "not_found" };
1084
+ }
1085
+ if (account.providerId !== ZCODE_START_PLAN_PROVIDER_ID) {
1086
+ set.status = 400;
1087
+ return {
1088
+ error: `Gift plans are only claimable for ${ZCODE_START_PLAN_PROVIDER_ID}`,
1089
+ code: "gifts_unsupported",
1090
+ };
1091
+ }
1092
+ let result: ZcodeGiftResult;
1093
+ try {
1094
+ const credential = await refreshDeps.resolveCredential(account.providerId as ProviderId, account.id);
1095
+ const fetcher = networkBindingFactory
1096
+ ? await dailyCheckinEgressForPass({ db, networkBindingFactory })(account.id, access.tenantId)
1097
+ : globalThis.fetch;
1098
+ result = await claimZcodeGifts({ accountId: account.id, credential, fetcher });
1099
+ } catch {
1100
+ set.status = 502;
1101
+ return {
1102
+ ok: false, status: 502,
1103
+ data: { id: account.id, state: "error", claimed: [], detail: "Gift claim request failed" },
1104
+ message: "Gift claim request failed", code: "gift_claim_failed",
1105
+ };
1106
+ }
1107
+ if (result.claimed.length > 0) {
1108
+ // Cold readers must not resurrect the balance from before the grant.
1109
+ await db.update(providerAccounts).set({ quotaSnapshot: null }).where(
1110
+ and(eq(providerAccounts.id, account.id), eq(providerAccounts.tenantId, access.tenantId)),
1111
+ );
1112
+ await invalidateQuotaCacheForAccount(account.id, redis);
1113
+ await snapshotInvalidator?.invalidate();
1114
+ }
1115
+ if (result.state === "error") {
1116
+ set.status = 502;
1117
+ return {
1118
+ ok: false,
1119
+ status: 502,
1120
+ data: { id: account.id, ...result },
1121
+ message: result.detail ?? "Gift claim failed",
1122
+ code: "gift_claim_failed",
1123
+ };
1124
+ }
1125
+ return {
1126
+ ok: true,
1127
+ status: 200,
1128
+ data: { id: account.id, ...result },
1129
+ message:
1130
+ result.state === "nothing"
1131
+ ? "No gift plan to claim"
1132
+ : result.state === "claimed"
1133
+ ? `Claimed ${result.claimed.join(", ")}`
1134
+ : result.state === "already_claimed"
1135
+ ? "Every gift plan was claimed before"
1136
+ : (result.detail ?? "Claim it in the ZCode app"),
1137
+ };
1138
+ })
957
1139
  .post("/accounts/:id/activity-report", async ({ request, params, set }) => {
958
1140
  const access = requireTenantScope(accessResolver(request), "dashboard:write");
959
1141
  const rows = await db
@@ -1002,6 +1184,12 @@ export function registerAccountQuotaTenantRoutes(
1002
1184
  // The growth event scores by uid, and only a JWT carries one. An
1003
1185
  // opaque API key must fail closed here — reporting it under another
1004
1186
  // account's uid would poison someone else's streak.
1187
+ // dynamic-import: B-02 lazy graph — buddy uid decoder on this buddy-only surface.
1188
+ // The buddy-oauth-shared graph is lazy-loaded: the uid decoder is only
1189
+ // needed on this buddy-only surface (B-02).
1190
+ const { buddyAccountUid } = await import(
1191
+ "../../providers/integrations/buddy/buddy-oauth-shared"
1192
+ );
1005
1193
  const userId = buddyAccountUid(credential);
1006
1194
  if (!userId) {
1007
1195
  set.status = 400;
@@ -1068,7 +1256,12 @@ export function registerAccountQuotaTenantRoutes(
1068
1256
  account.providerId as ProviderId,
1069
1257
  account.id,
1070
1258
  );
1071
- const list = await listAccountResetCredits(account.providerId, credential);
1259
+ // Cached per account for a short TTL: every dialog open was hitting the
1260
+ // upstream live. The cache is cleared on a successful redemption, so a
1261
+ // consumed credit is not shown again for the rest of the TTL.
1262
+ const list = await getCachedResetCredits(account.providerId, account.id, () =>
1263
+ listAccountResetCredits(account.providerId, credential),
1264
+ );
1072
1265
  return list ?? { availableCount: 0, credits: [] };
1073
1266
  } catch (err) {
1074
1267
  return { availableCount: 0, credits: [] };