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
@@ -0,0 +1,308 @@
1
+ /**
2
+ * Background execution for the manual daily-growth batch (`POST /quota/daily-growth`).
3
+ *
4
+ * The route used to run the whole batch synchronously — chunked 3-parallel,
5
+ * up to two upstream HTTP calls per account, no global timeout — so a large
6
+ * roster held the console request open and blocked the event loop. This module
7
+ * moves the orchestration to an in-process background job: the route enqueues
8
+ * and returns `{ jobId }` immediately, and a status route reads the job's
9
+ * progress.
10
+ *
11
+ * The per-account runner (report then claim, egress rotation, per-leg timeout)
12
+ * is injected by the route — the job owns only the orchestration: the
13
+ * bounded-concurrency worker loop, the single-result aggregation, and the
14
+ * deduplication guards. This keeps the module free of the route's imports and
15
+ * avoids a cycle between the two quota modules.
16
+ *
17
+ * One job per (tenant, provider) key is allowed at a time. The key is the
18
+ * double-claim guard between two manual triggers: enqueuing again while a job
19
+ * for the same tenant+provider is still queued/running returns the existing
20
+ * job id instead of stamping the same accounts twice. In parallel with the
21
+ * daily sweep, the ledger is the guard — the runner stamps
22
+ * `markCheckinAttempted` after a successful claim, and this runner skips
23
+ * accounts the sweep (or an earlier run) already settled today, so the same
24
+ * account is never claimed twice in one window.
25
+ *
26
+ * The registry is intentionally process-local: the manual trigger is an
27
+ * operator action on a single gateway, so a cross-process queue would buy
28
+ * nothing here.
29
+ */
30
+ import { randomUUID } from "node:crypto";
31
+ import type { CartethyiaDatabase } from "../../persistence/db-handle";
32
+ import type { RedisClient } from "../../persistence/redis";
33
+ import { checkinAttemptedToday } from "../../workers/daily-checkin";
34
+ import type { ValidatedFetch } from "../../network/outbound-fetch";
35
+
36
+ /** Lifecycle states of a daily-growth job. */
37
+ export type DailyGrowthJobState = "queued" | "running" | "done" | "failed";
38
+
39
+ /** One account the job failed to settle, with the reason. */
40
+ export interface DailyGrowthJobError {
41
+ readonly accountId: string;
42
+ readonly error: string;
43
+ }
44
+
45
+ /** Aggregate outcome of one job. */
46
+ export interface DailyGrowthJobResult {
47
+ /** Accounts the job actually attempted (excludes skipped/already-settled). */
48
+ readonly total: number;
49
+ readonly succeeded: number;
50
+ readonly failed: number;
51
+ readonly errors: readonly DailyGrowthJobError[];
52
+ }
53
+
54
+ /** Public view of a job, as the status route returns it. */
55
+ export interface DailyGrowthJobView {
56
+ readonly jobId: string;
57
+ readonly state: DailyGrowthJobState;
58
+ readonly providerId: string;
59
+ readonly createdAt: string;
60
+ readonly finishedAt: string | null;
61
+ readonly total: number;
62
+ readonly succeeded: number;
63
+ readonly failed: number;
64
+ readonly errors: readonly DailyGrowthJobError[];
65
+ }
66
+
67
+ /** One account the job will run the daily-growth pass over. */
68
+ export interface DailyGrowthJobAccount {
69
+ readonly id: string;
70
+ readonly tenantId: string | null;
71
+ readonly label: string;
72
+ }
73
+
74
+ /** Outcome the route's per-account runner reports. */
75
+ export interface DailyGrowthAccountOutcome {
76
+ readonly ok: boolean;
77
+ /** Present when `ok` is false: the reason the account was not settled. */
78
+ readonly error?: string;
79
+ }
80
+
81
+ /** Everything the background runner needs to execute one job. */
82
+ export interface DailyGrowthJobDeps {
83
+ readonly db: CartethyiaDatabase;
84
+ readonly redis: RedisClient | undefined;
85
+ /** Runs one account's daily-growth pass (report then claim). */
86
+ readonly runAccount: (account: DailyGrowthJobAccount) => Promise<DailyGrowthAccountOutcome>;
87
+ /** Invalidated once when the job finishes, so the page re-reads the ledger. */
88
+ readonly snapshotInvalidator?: { invalidate(): Promise<number> };
89
+ }
90
+
91
+ /** Bound on accounts processed at once. Mirrors the old synchronous chunk size. */
92
+ const DAILY_GROWTH_CONCURRENCY = 3;
93
+ /**
94
+ * Per-account leg budget. Each individual upstream call already carries its own
95
+ * 15s abort timeout; this bounds the whole leg (credential resolve + report +
96
+ * check-in) so a single stuck account cannot hold a job worker open forever.
97
+ */
98
+ export const DAILY_GROWTH_LEG_TIMEOUT_MS = 60_000;
99
+
100
+ /**
101
+ * Wraps a fetcher so every request also carries `budget`, even when the callee
102
+ * sets its own per-request timeout. The buddy/qoder billing helpers pass an
103
+ * `AbortSignal.timeout(...)` of their own, which would override a plain
104
+ * `signalFetch`-style injection; combining signals intersects both bounds.
105
+ */
106
+ export function withLegBudget(
107
+ fetcher: ValidatedFetch | typeof fetch,
108
+ budget: AbortSignal,
109
+ ): typeof fetch {
110
+ const wrapped = (
111
+ input: Parameters<typeof fetch>[0],
112
+ init?: Parameters<typeof fetch>[1],
113
+ ): Promise<Response> =>
114
+ fetcher(input, {
115
+ ...init,
116
+ signal: init?.signal ? AbortSignal.any([budget, init.signal]) : budget,
117
+ });
118
+ return Object.assign(wrapped, {
119
+ preconnect: (fetcher as typeof fetch).preconnect,
120
+ });
121
+ }
122
+
123
+ /** Registry cap; oldest finished jobs are evicted so the map cannot grow unbounded. */
124
+ const DAILY_GROWTH_JOB_MAX = 64;
125
+
126
+ interface DailyGrowthJobRecord {
127
+ readonly id: string;
128
+ readonly key: string;
129
+ readonly tenantId: string;
130
+ readonly providerId: string;
131
+ readonly createdAt: number;
132
+ state: DailyGrowthJobState;
133
+ finishedAt: number | null;
134
+ readonly accounts: readonly DailyGrowthJobAccount[];
135
+ result: DailyGrowthJobResult;
136
+ }
137
+
138
+ const jobs = new Map<string, DailyGrowthJobRecord>();
139
+ /** tenant+provider key → job id; the double-trigger guard. */
140
+ const jobsByKey = new Map<string, string>();
141
+ /**
142
+ * Accounts currently being worked by any job. Global accounts (`tenant_id IS
143
+ * NULL`) are visible to every tenant, so two tenants can enqueue jobs over the
144
+ * same global account; this set keeps them from stamping it twice in the same
145
+ * window. Tenant-owned accounts cannot collide this way (they are unique to
146
+ * their tenant), but the set is uniform and cheap.
147
+ */
148
+ const inFlightAccounts = new Set<string>();
149
+
150
+ function jobView(job: DailyGrowthJobRecord): DailyGrowthJobView {
151
+ return {
152
+ jobId: job.id,
153
+ state: job.state,
154
+ providerId: job.providerId,
155
+ createdAt: new Date(job.createdAt).toISOString(),
156
+ finishedAt: job.finishedAt === null ? null : new Date(job.finishedAt).toISOString(),
157
+ total: job.result.total,
158
+ succeeded: job.result.succeeded,
159
+ failed: job.result.failed,
160
+ errors: job.result.errors,
161
+ };
162
+ }
163
+
164
+ /** Evicts the oldest finished jobs once the registry exceeds its cap. */
165
+ function evictFinishedJobs(): void {
166
+ if (jobs.size <= DAILY_GROWTH_JOB_MAX) return;
167
+ const finished: DailyGrowthJobRecord[] = [];
168
+ for (const job of jobs.values()) {
169
+ if (job.state === "done" || job.state === "failed") finished.push(job);
170
+ }
171
+ finished.sort((left, right) => left.createdAt - right.createdAt);
172
+ let excess = jobs.size - DAILY_GROWTH_JOB_MAX;
173
+ for (const job of finished) {
174
+ if (excess <= 0) break;
175
+ jobs.delete(job.id);
176
+ if (jobsByKey.get(job.key) === job.id) jobsByKey.delete(job.key);
177
+ excess -= 1;
178
+ }
179
+ }
180
+
181
+ /**
182
+ * Runs one job to completion. Never rejects: every failure is collected into
183
+ * the job's result, so the status route always has an answer.
184
+ */
185
+ async function runJob(record: DailyGrowthJobRecord, deps: DailyGrowthJobDeps): Promise<void> {
186
+ record.state = "running";
187
+ const succeeded: string[] = [];
188
+ const errors: DailyGrowthJobError[] = [];
189
+ const attempted: string[] = [];
190
+ let cursor = 0;
191
+ const accounts = record.accounts;
192
+
193
+ const runOne = async (account: DailyGrowthJobAccount): Promise<void> => {
194
+ // Double-claim guard with the daily sweep (and any other job): the ledger
195
+ // is shared, so an account the sweep already settled today is skipped
196
+ // rather than claimed again — the same `markCheckinAttempted` semantics
197
+ // the button uses.
198
+ if (inFlightAccounts.has(account.id)) return;
199
+ try {
200
+ if (await checkinAttemptedToday({ db: deps.db, redis: deps.redis, accountId: account.id })) {
201
+ return;
202
+ }
203
+ } catch {
204
+ // An unreadable ledger reads as "not attempted" — the safe direction for
205
+ // a manual trigger, which asserts "try now".
206
+ }
207
+ inFlightAccounts.add(account.id);
208
+ attempted.push(account.id);
209
+ try {
210
+ const outcome = await deps.runAccount(account);
211
+ if (outcome.ok) {
212
+ succeeded.push(account.id);
213
+ } else {
214
+ errors.push({ accountId: account.id, error: outcome.error ?? "Daily growth failed" });
215
+ }
216
+ } finally {
217
+ inFlightAccounts.delete(account.id);
218
+ }
219
+ };
220
+
221
+ await Promise.all(
222
+ Array.from({ length: Math.min(DAILY_GROWTH_CONCURRENCY, accounts.length) }, async () => {
223
+ for (;;) {
224
+ const index = cursor;
225
+ cursor += 1;
226
+ if (index >= accounts.length) return;
227
+ await runOne(accounts[index]!);
228
+ }
229
+ }),
230
+ );
231
+
232
+ record.result = {
233
+ total: attempted.length,
234
+ succeeded: succeeded.length,
235
+ failed: errors.length,
236
+ errors,
237
+ };
238
+ record.finishedAt = Date.now();
239
+ record.state = "done";
240
+ if (deps.snapshotInvalidator) {
241
+ try {
242
+ await deps.snapshotInvalidator.invalidate();
243
+ } catch {
244
+ // Best-effort: a failed snapshot invalidate must not fail the job.
245
+ }
246
+ }
247
+ }
248
+
249
+ /**
250
+ * Enqueues a daily-growth job and returns its id immediately. When a job for
251
+ * the same (tenant, provider) is already queued or running, the existing job
252
+ * id is returned instead — the operator pressed the button twice, and running
253
+ * the same accounts twice would double-claim.
254
+ */
255
+ export function enqueueDailyGrowthJob(args: {
256
+ readonly deps: DailyGrowthJobDeps;
257
+ readonly tenantId: string;
258
+ readonly providerId: string;
259
+ readonly accounts: readonly DailyGrowthJobAccount[];
260
+ }): string {
261
+ const key = `${args.tenantId}:${args.providerId}`;
262
+ const existing = jobsByKey.get(key);
263
+ if (existing !== undefined) {
264
+ const job = jobs.get(existing);
265
+ if (job !== undefined && (job.state === "queued" || job.state === "running")) {
266
+ return job.id;
267
+ }
268
+ jobsByKey.delete(key);
269
+ }
270
+
271
+ const id = randomUUID();
272
+ const record: DailyGrowthJobRecord = {
273
+ id,
274
+ key,
275
+ tenantId: args.tenantId,
276
+ providerId: args.providerId,
277
+ createdAt: Date.now(),
278
+ state: "queued",
279
+ finishedAt: null,
280
+ accounts: args.accounts,
281
+ result: { total: 0, succeeded: 0, failed: 0, errors: [] },
282
+ };
283
+ jobs.set(id, record);
284
+ jobsByKey.set(key, id);
285
+ evictFinishedJobs();
286
+ void runJob(record, args.deps).catch(() => {
287
+ record.state = "failed";
288
+ record.finishedAt = Date.now();
289
+ });
290
+ return id;
291
+ }
292
+
293
+ /**
294
+ * Reads one job's status. Scoped to the requesting tenant: a job enqueued by
295
+ * another tenant is not visible, so one tenant cannot probe another's work.
296
+ */
297
+ export function getDailyGrowthJob(jobId: string, tenantId: string): DailyGrowthJobView | null {
298
+ const job = jobs.get(jobId);
299
+ if (job === undefined || job.tenantId !== tenantId) return null;
300
+ return jobView(job);
301
+ }
302
+
303
+ /** Test seam: clears the registry so suites do not leak state across runs. */
304
+ export function clearDailyGrowthJobsForTests(): void {
305
+ jobs.clear();
306
+ jobsByKey.clear();
307
+ inFlightAccounts.clear();
308
+ }
@@ -20,7 +20,7 @@ import { ConsoleDomainError, requireGlobalAdmin, requireTenantScope } from "../s
20
20
  import type { ConsoleAccessResolver } from "../auth/access";
21
21
 
22
22
  /** True when `token` looks like a plausible machine token. */
23
- export function isValidNativeIdentityToken(token: string): boolean {
23
+ function isValidNativeIdentityToken(token: string): boolean {
24
24
  return typeof token === "string" && token.length >= 8 && token.length <= 512;
25
25
  }
26
26
 
@@ -120,7 +120,7 @@ export function createNativeIdentityOps(
120
120
  * (`tenant_id IS NULL`) need platform admin; tenant-owned accounts need the
121
121
  * tenant scope for the owning tenant.
122
122
  */
123
- export interface NativeIdentityScope {
123
+ interface NativeIdentityScope {
124
124
  readonly tenantId: string | null;
125
125
  }
126
126
 
@@ -11,7 +11,7 @@ import {
11
11
  } from "../../providers/quota/refresh";
12
12
 
13
13
  /** Shape returned for quota data on dashboard account responses. */
14
- export interface QuotaRefreshView {
14
+ interface QuotaRefreshView {
15
15
  readonly source: string;
16
16
  readonly status: "ready" | "error" | "refreshing";
17
17
  readonly plan: string | null;
@@ -14,7 +14,7 @@ import { consoleErrorHandler, requireTenantScope } from "../shared/errors";
14
14
  import { REMOTE_SUPPORT_TOOLS } from "./contracts";
15
15
  import type { RemoteSupportService } from "./service";
16
16
 
17
- export interface RemoteSupportRoutesConfig {
17
+ interface RemoteSupportRoutesConfig {
18
18
  readonly accessResolver: ConsoleAccessResolver;
19
19
  readonly service: RemoteSupportService;
20
20
  }
@@ -33,7 +33,7 @@ const URL_POLL_MS = 500;
33
33
  /** Cached install detection so we don't spawn `where`/`which` on every poll. */
34
34
  const installedCache = new Map<RemoteSupportTool, boolean>();
35
35
 
36
- export interface RemoteSupportServiceConfig {
36
+ interface RemoteSupportServiceConfig {
37
37
  /** Defaults to the gateway's configured port. */
38
38
  readonly port: number;
39
39
  /** Overridable for tests. Defaults to a subdir of the user config dir. */
@@ -245,7 +245,7 @@ function tailscaleDnsName(): string | null {
245
245
  * nothing about this: stopping the tunnel leaves the node up and empties the
246
246
  * status, which is why an online node alone was read as "still active".
247
247
  */
248
- export function tailscaleServing(): boolean {
248
+ function tailscaleServing(): boolean {
249
249
  const result = spawnSync("tailscale", ["serve", "status", "--json"], { encoding: "utf8", windowsHide: true });
250
250
  if (result.status !== 0) return false;
251
251
  let parsed: unknown;
@@ -102,7 +102,7 @@ export interface ModelRoutingStore {
102
102
 
103
103
  /** Structurally satisfied by `InMemoryRouteSnapshotService` as-is — no import
104
104
  * of routing internals into the console domain layer. */
105
- export interface ModelRoutingSnapshotInvalidator {
105
+ interface ModelRoutingSnapshotInvalidator {
106
106
  invalidate(): Promise<number>;
107
107
  }
108
108
 
@@ -223,7 +223,7 @@ export type PoolStatus = (typeof POOL_STATUSES)[number];
223
223
  * stay aligned with the `pool_routing_strategy` enum in `schema.ts`.
224
224
  */
225
225
  export const POOL_ROUTING_STRATEGIES = ["least_loaded", "round_robin", "random"] as const;
226
- export type PoolRoutingStrategyValue = (typeof POOL_ROUTING_STRATEGIES)[number];
226
+ type PoolRoutingStrategyValue = (typeof POOL_ROUTING_STRATEGIES)[number];
227
227
 
228
228
  export interface PoolStrategySetting {
229
229
  strategy: PoolRoutingStrategyValue;
@@ -456,7 +456,7 @@ export const MAX_BATCH_PROBE_TARGETS = 100;
456
456
  */
457
457
  export const PROVIDER_PROBE_BATCH_CONCURRENCY = 4;
458
458
 
459
- export interface NetworkPoolAgentReleaser {
459
+ interface NetworkPoolAgentReleaser {
460
460
  releasePool(poolId: string, tenantId: string): Promise<void>;
461
461
  }
462
462
  export interface NetworkPoolConfig {
@@ -18,7 +18,7 @@
18
18
  import { POOL_KINDS, POOL_STATUSES } from "./contracts";
19
19
 
20
20
  /** One row that parsed, ready to become a pool. */
21
- export interface ParsedProxyRow {
21
+ interface ParsedProxyRow {
22
22
  readonly kind: (typeof POOL_KINDS)[number];
23
23
  readonly endpoint: string;
24
24
  readonly credential: string | null;
@@ -26,12 +26,12 @@ export interface ParsedProxyRow {
26
26
  }
27
27
 
28
28
  /** One row that did not parse, with the reason. */
29
- export interface SkippedProxyRow {
29
+ interface SkippedProxyRow {
30
30
  readonly line: number;
31
31
  readonly reason: string;
32
32
  }
33
33
 
34
- export interface ParsedProxyFile {
34
+ interface ParsedProxyFile {
35
35
  readonly rows: readonly ParsedProxyRow[];
36
36
  readonly skipped: readonly SkippedProxyRow[];
37
37
  }
@@ -36,7 +36,7 @@ export const RELAY_TARGETS: readonly RelayTarget[] = ["cloudflare", "vercel", "d
36
36
  * The same handler body runs on Workers and Vercel Edge (`export default {
37
37
  * fetch }`); the Deno entry wraps it in `Deno.serve`.
38
38
  */
39
- export const RELAY_WORKER_SOURCE = `// Cartethyia hosted relay.
39
+ const RELAY_WORKER_SOURCE = `// Cartethyia hosted relay.
40
40
  //
41
41
  // Forwards a request to the origin named by the x-relay-target header, at the
42
42
  // path named by x-relay-path. Deployed to Cloudflare Workers / Vercel Edge.
@@ -83,7 +83,7 @@ export default handler;
83
83
  `;
84
84
 
85
85
  /** A Deno Deploy entry wraps the shared handler in `Deno.serve`. */
86
- export const RELAY_DENO_SOURCE = `${RELAY_WORKER_SOURCE}
86
+ const RELAY_DENO_SOURCE = `${RELAY_WORKER_SOURCE}
87
87
  Deno.serve((request) => handler.fetch(request));
88
88
  `;
89
89
 
@@ -111,7 +111,7 @@ export interface RelayDeployResult {
111
111
  }
112
112
 
113
113
  /** Outbound fetch seam (the validated fetch), so tests can stub deploys. */
114
- export type RelayFetch = (url: string, init: RequestInit) => Promise<Response>;
114
+ type RelayFetch = (url: string, init: RequestInit) => Promise<Response>;
115
115
 
116
116
  function generateName(prefix: string): string {
117
117
  return `${prefix}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`;
@@ -53,6 +53,12 @@ export interface RuntimeSettingsResponse {
53
53
  readonly telemetryWindowMinutes: number | null;
54
54
  /** Per-tenant inline attachment ceiling in bytes; null means deployment default. */
55
55
  readonly imageClampMaxBytes: number | null;
56
+ /**
57
+ * Ordered vision-model ids used to describe images when the selected model
58
+ * is not image-capable. Empty means the legacy `[image]` placeholder is
59
+ * used instead.
60
+ */
61
+ readonly visionHandoffModels: readonly string[];
56
62
  readonly updatedAt: string;
57
63
  /*
58
64
  * What each `null` actually resolves to, in the same units the row stores.
@@ -67,7 +73,7 @@ export interface RuntimeSettingsResponse {
67
73
  }
68
74
 
69
75
  /** The deployment-level values a `null` runtime setting falls back to. */
70
- export interface RuntimeSettingsDefaults {
76
+ interface RuntimeSettingsDefaults {
71
77
  /** Seconds; from `CARTETHYIA_UPSTREAM_TIMEOUT_MS`. */
72
78
  readonly upstreamTimeoutSeconds: number;
73
79
  /** Minutes; what the payload store expires frames after. */
@@ -122,7 +128,7 @@ export type TelemetryPayloadMode = (typeof TELEMETRY_PAYLOAD_MODES)[number];
122
128
  export const TELEMETRY_PAYLOAD_DEPTHS = ["minimum", "moderate", "maximum"] as const;
123
129
  /** Capture depth for `full` mode accepted by the runtime-settings PATCH. */
124
130
  export type TelemetryPayloadDepth = (typeof TELEMETRY_PAYLOAD_DEPTHS)[number];
125
- export const PRIVACY_MODES = ["masked", "full"] as const;
131
+ const PRIVACY_MODES = ["masked", "full"] as const;
126
132
  /** Provider/model label privacy accepted by the runtime-settings PATCH. */
127
133
  export type PrivacyMode = (typeof PRIVACY_MODES)[number];
128
134
  /** PonyTail directive intensity accepted by the runtime-settings PATCH. */
@@ -130,6 +136,9 @@ export type PonyTailLevel = (typeof PONYTAIL_LEVELS)[number];
130
136
  /** RTK prune strength accepted by the runtime-settings PATCH. */
131
137
  export type RtkLevel = (typeof RTK_LEVELS)[number];
132
138
 
139
+ /** Upper bound on the number of ordered vision-handoff models. */
140
+ export const VISION_HANDOFF_MAX_MODELS = 8;
141
+
133
142
  export function createRuntimeSettingsOperations(deps: RuntimeSettingsConfig) {
134
143
  const operations = {
135
144
  async get(access: AccessDecision | undefined): Promise<RuntimeSettingsResponse> {
@@ -178,6 +187,23 @@ export function createRuntimeSettingsOperations(deps: RuntimeSettingsConfig) {
178
187
  if (patch.rtkPruneLevel !== undefined && !RTK_LEVELS.includes(patch.rtkPruneLevel)) {
179
188
  throw new ConsoleDomainError("invalid_request", 400, "Invalid rtkPruneLevel");
180
189
  }
190
+ if (patch.visionHandoffModels !== undefined) {
191
+ if (!Array.isArray(patch.visionHandoffModels)) {
192
+ throw new ConsoleDomainError("invalid_request", 400, "visionHandoffModels must be an array of model ids");
193
+ }
194
+ if (patch.visionHandoffModels.length > VISION_HANDOFF_MAX_MODELS) {
195
+ throw new ConsoleDomainError(
196
+ "invalid_request",
197
+ 400,
198
+ `visionHandoffModels may list at most ${VISION_HANDOFF_MAX_MODELS} models`,
199
+ );
200
+ }
201
+ for (const model of patch.visionHandoffModels) {
202
+ if (typeof model !== "string" || model.trim().length === 0) {
203
+ throw new ConsoleDomainError("invalid_request", 400, "visionHandoffModels entries must be non-empty model ids");
204
+ }
205
+ }
206
+ }
181
207
  const updated = await deps.store.update(a.tenantId, patch);
182
208
  await deps.auditSink?.record({
183
209
  access: a,
@@ -245,4 +271,5 @@ export const runtimeUpdateBody = t.Object({
245
271
  imageClampMaxBytes: t.Optional(
246
272
  t.Union([t.Null(), t.Number({ minimum: 64 * 1024, maximum: 100 * 1024 * 1024 })]),
247
273
  ),
274
+ visionHandoffModels: t.Optional(t.Array(t.String())),
248
275
  });
@@ -15,6 +15,7 @@ import {
15
15
  RTK_LEVELS,
16
16
  TELEMETRY_PAYLOAD_DEPTHS,
17
17
  TELEMETRY_PAYLOAD_MODES,
18
+ VISION_HANDOFF_MAX_MODELS,
18
19
  type PonyTailLevel,
19
20
  type ResponsesReasoningSummary,
20
21
  type RtkLevel,
@@ -115,6 +116,22 @@ function normalizeConcurrencyTolerance(value: unknown): number | null {
115
116
  return normalizeBoundedNumber(value, 0, 1024);
116
117
  }
117
118
 
119
+ /** Ordered vision-handoff models: non-empty strings, trimmed, de-duplicated. */
120
+ function normalizeVisionHandoffModels(value: unknown): readonly string[] {
121
+ if (!Array.isArray(value)) return [];
122
+ const seen = new Set<string>();
123
+ const out: string[] = [];
124
+ for (const entry of value) {
125
+ if (typeof entry !== "string") continue;
126
+ const model = entry.trim();
127
+ if (model.length === 0 || seen.has(model)) continue;
128
+ seen.add(model);
129
+ out.push(model);
130
+ if (out.length >= VISION_HANDOFF_MAX_MODELS) break;
131
+ }
132
+ return out;
133
+ }
134
+
118
135
  function mapRuntimeSettingsRow(
119
136
  row: typeof consoleSettings.$inferSelect | undefined,
120
137
  redisBackend: RedisBackend,
@@ -148,6 +165,7 @@ function mapRuntimeSettingsRow(
148
165
  upstreamTimeoutSeconds: normalizeUpstreamTimeoutSeconds(prefs.upstreamTimeoutSeconds),
149
166
  telemetryWindowMinutes: normalizeTelemetryWindowMinutes(prefs.telemetryWindowMinutes),
150
167
  imageClampMaxBytes: normalizeImageClampMaxBytes(prefs.imageClampMaxBytes),
168
+ visionHandoffModels: normalizeVisionHandoffModels(prefs.visionHandoffModels),
151
169
  updatedAt,
152
170
  /*
153
171
  * Resolved at request time, not at module load: all four come from env the
@@ -211,6 +229,8 @@ export class DrizzleRuntimeSettingsStore implements RuntimeSettingsStore {
211
229
  );
212
230
  if (patch.imageClampMaxBytes !== undefined)
213
231
  patchPrefs.imageClampMaxBytes = normalizeImageClampMaxBytes(patch.imageClampMaxBytes);
232
+ if (patch.visionHandoffModels !== undefined)
233
+ patchPrefs.visionHandoffModels = normalizeVisionHandoffModels(patch.visionHandoffModels);
214
234
 
215
235
  const rows = await this.db
216
236
  .insert(consoleSettings)