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
@@ -41,6 +41,77 @@ const MIN_TOKEN_LENGTH = 20;
41
41
  */
42
42
  const SHARE_STATS_STREAM_INTERVAL_MS = 30_000;
43
43
 
44
+ /**
45
+ * A subscriber of the process-wide share-stats sampler. A recipient is keyed
46
+ * by its family (`tenantId` + template key) so one sampler tick can push to
47
+ * many links without a per-connection timer.
48
+ */
49
+ type ShareStatsListener = (family: ShareFamilyStats) => void;
50
+
51
+ /**
52
+ * One sampler per gateway for share-stats SSE delivery. Previously each
53
+ * `/share/:token/stats/stream` connection ran its own 30s timer and re-read
54
+ * the family rollup; with N open tabs that is N timers and N rollups all
55
+ * reading the same data. Now a single process-level sampler ticks on the
56
+ * shared cadence and fans each computed rollup out to the subscribers whose
57
+ * family it matches. Timers run only while at least one stream is open.
58
+ */
59
+ function createShareStatsSampler(config: {
60
+ readonly db: CartethyiaDatabase;
61
+ readonly shareStore: ShareLinkStore;
62
+ readonly stats: ShareStatsPort;
63
+ }): { subscribe(token: string, listener: ShareStatsListener): () => void } {
64
+ const subscribers = new Map<string, Set<ShareStatsListener>>();
65
+ let timer: ReturnType<typeof setTimeout> | undefined;
66
+
67
+ const tick = async (): Promise<void> => {
68
+ // One resolve per family token: a recipient with several tabs shares one
69
+ // rollup, and a family with no open stream is never computed.
70
+ const tokens = new Set(subscribers.keys());
71
+ for (const token of tokens) {
72
+ const listeners = subscribers.get(token);
73
+ if (listeners === undefined || listeners.size === 0) continue;
74
+ try {
75
+ const family = await resolveFamilyStats(config.db, config.shareStore, config.stats, token);
76
+ if (family === null) continue;
77
+ for (const listener of listeners) listener(family);
78
+ } catch {
79
+ // A transient read must not interrupt the sampler.
80
+ }
81
+ }
82
+ };
83
+
84
+ const start = (): void => {
85
+ if (timer !== undefined) return;
86
+ timer = setTimeout(async function run() {
87
+ timer = setTimeout(run, SHARE_STATS_STREAM_INTERVAL_MS);
88
+ await tick();
89
+ }, SHARE_STATS_STREAM_INTERVAL_MS);
90
+ };
91
+
92
+ const stop = (): void => {
93
+ if (timer !== undefined) clearTimeout(timer);
94
+ timer = undefined;
95
+ };
96
+
97
+ return {
98
+ subscribe(token, listener) {
99
+ let listeners = subscribers.get(token);
100
+ if (listeners === undefined) {
101
+ listeners = new Set();
102
+ subscribers.set(token, listeners);
103
+ }
104
+ listeners.add(listener);
105
+ if (subscribers.size > 0) start();
106
+ return () => {
107
+ listeners.delete(listener);
108
+ if (listeners.size === 0) subscribers.delete(token);
109
+ if (subscribers.size === 0) stop();
110
+ };
111
+ },
112
+ };
113
+ }
114
+
44
115
  /**
45
116
  * Context and capabilities for one allowed model, sourced from the public
46
117
  * catalog. Every field is nullable/optional so the page can show what is known
@@ -56,7 +127,7 @@ export interface ShareModelInfo {
56
127
  readonly webSearch: boolean;
57
128
  }
58
129
 
59
- export interface ShareRouterOptions {
130
+ interface ShareRouterOptions {
60
131
  readonly db: CartethyiaDatabase;
61
132
  readonly shareStore: ShareLinkStore;
62
133
  /** Resolves the normalized client IP through the trusted-proxy boundary. */
@@ -263,6 +334,12 @@ async function resolveFamilyStats(
263
334
  /** Creates the public enrollment page and one-time shared-key issuance route. */
264
335
  export function createShareRouter(options: ShareRouterOptions): AnyElysia {
265
336
  const { db, shareStore } = options;
337
+ // The sampler is shared by every stats stream this router serves; it is a
338
+ // no-op when the composition omits `stats` (tests that do not exercise the
339
+ // stream), because `tick` never runs with no subscribers.
340
+ const statsSampler = options.stats === undefined
341
+ ? undefined
342
+ : createShareStatsSampler({ db, shareStore, stats: options.stats });
266
343
 
267
344
  return new Elysia()
268
345
  /**
@@ -296,6 +373,7 @@ export function createShareRouter(options: ShareRouterOptions): AnyElysia {
296
373
  oneTimeLimit: row.lifetimeTokenBudget,
297
374
  requestsPerMinute: row.requestsPerMinute,
298
375
  maxConcurrentRequests: row.maxConcurrentRequests,
376
+ concurrencyScope: row.concurrencyScope ?? "family",
299
377
  modelPrefix: row.modelPrefix,
300
378
  models,
301
379
  modelInfo,
@@ -442,48 +520,38 @@ export function createShareRouter(options: ShareRouterOptions): AnyElysia {
442
520
  /**
443
521
  * Live family activity for the share page's stats section, pushed over SSE.
444
522
  *
445
- * The stream sends one snapshot immediately on connect, then re-reads on a
446
- * short cadence so the recipient watches the shared quota move instead of
447
- * refreshing. Telemetry is buffered server-side and drained in under a
448
- * second, so a DB re-read on this cadence is as fresh as an event bus would
449
- * be. Same bearer token and same masked-IP rollup as the one-shot route; a
450
- * token that stops resolving closes the stream rather than reporting zeros.
523
+ * The stream sends one snapshot immediately on connect, then pushes on a
524
+ * shared sampler cadence so the recipient watches the shared quota move
525
+ * instead of refreshing. Telemetry is buffered server-side and drained in
526
+ * under a second, so the sampler's DB re-read is as fresh as an event bus
527
+ * would be. Same bearer token and same masked-IP rollup as the one-shot
528
+ * route; a token that stops resolving closes the stream rather than
529
+ * reporting zeros.
451
530
  */
452
531
  .get("/share/:token/stats/stream", async ({ params, request }) => {
453
532
  if (typeof params.token !== "string") return notFound();
454
533
  const token = params.token;
455
534
  if (token.length < MIN_TOKEN_LENGTH) return notFound();
456
535
  const stats = options.stats;
457
- if (!stats) return json({ error: { code: "stats_unavailable", message: "Stats are unavailable" } }, 503);
536
+ if (!stats || statsSampler === undefined) {
537
+ return json({ error: { code: "stats_unavailable", message: "Stats are unavailable" } }, 503);
538
+ }
539
+ const sampler = statsSampler;
458
540
  return consoleSseResponse(
459
541
  createConsoleSseStream(request.signal, (sender) => {
460
- let closed = false;
461
- let timer: ReturnType<typeof setTimeout> | undefined;
462
- const push = async () => {
463
- const family = await resolveFamilyStats(db, shareStore, stats, token);
464
- if (closed) return;
542
+ const unsubscribe = sampler.subscribe(token, (family) => sender.send("stats", family));
543
+ // Bootstrap: deliver the link's current rollup immediately, and close
544
+ // the stream if the token no longer resolves — the same terminal
545
+ // behavior the per-connection timer had on a dead link.
546
+ void resolveFamilyStats(db, shareStore, stats, token).then((family) => {
465
547
  if (family === null) {
466
548
  sender.send("error", { code: "link_not_found" });
467
- closed = true;
468
- return;
549
+ unsubscribe();
550
+ } else {
551
+ sender.send("stats", family);
469
552
  }
470
- sender.send("stats", family);
471
- };
472
- const schedule = () => {
473
- timer = setTimeout(() => {
474
- if (closed) return;
475
- void push().finally(() => {
476
- if (!closed) schedule();
477
- });
478
- }, SHARE_STATS_STREAM_INTERVAL_MS);
479
- };
480
- void push().finally(() => {
481
- if (!closed) schedule();
482
- });
483
- return () => {
484
- closed = true;
485
- clearTimeout(timer);
486
- };
553
+ }).catch(() => undefined);
554
+ return unsubscribe;
487
555
  }),
488
556
  );
489
557
  })
@@ -30,7 +30,7 @@ import { cachedRead } from "../shared/read-through-cache";
30
30
  import { utcDayStart, utcMonthStart } from "../shared/day";
31
31
 
32
32
  /** Totals for the whole share family, all-time plus the windows the UI draws. */
33
- export interface ShareFamilyTotals {
33
+ interface ShareFamilyTotals {
34
34
  readonly requests: number;
35
35
  readonly errors: number;
36
36
  readonly inputTokens: number;
@@ -45,7 +45,7 @@ export interface ShareFamilyTotals {
45
45
  }
46
46
 
47
47
  /** One hour bucket of the last 24 hours, oldest first. */
48
- export interface ShareHourlyBucket {
48
+ interface ShareHourlyBucket {
49
49
  /** ISO instant at the top of the hour. */
50
50
  readonly hour: string;
51
51
  readonly requests: number;
@@ -53,7 +53,7 @@ export interface ShareHourlyBucket {
53
53
  readonly tokens: number;
54
54
  }
55
55
 
56
- export interface ShareTopModel {
56
+ interface ShareTopModel {
57
57
  readonly modelId: string;
58
58
  readonly requests: number;
59
59
  readonly tokens: number;
@@ -63,7 +63,7 @@ export interface ShareTopModel {
63
63
  readonly avgTtfbMs: number | null;
64
64
  }
65
65
 
66
- export interface ShareTopClientIp {
66
+ interface ShareTopClientIp {
67
67
  /** Always masked: this payload is served to the recipient, not the tenant. */
68
68
  readonly ip: string;
69
69
  readonly requests: number;
@@ -371,11 +371,9 @@ async function computeFamilyStatsBody(
371
371
  .where(eq(apiKeys.id, templateKeyId))
372
372
  .limit(1),
373
373
  ]);
374
- // Cumulative family spend: the template's own counter plus the telemetry
375
- // total. The counter is a manually-configured seed (an operator limit), not
376
- // traffic, and it is never reset; azmetry rows for revoked/deleted children
377
- // likewise stay counted, so the running total is monotonic and consistent
378
- // across the overview, the share dialog, and this page.
374
+ // Cumulative family spend: the template's own counter is the live
375
+ // accumulator (updated by every recipient commit), so it IS the total.
376
+ // Telemetry still drives the bounded daily/monthly windows below.
379
377
  const templateLifetime = Number(template[0]?.lifetime ?? 0);
380
378
 
381
379
  // The ranked tables need real events, bounded to the 24-hour window. The
@@ -398,10 +396,10 @@ async function computeFamilyStatsBody(
398
396
  });
399
397
  rollup.totals = {
400
398
  ...rollup.totals,
401
- // The template counter is the seed on the running total, not a distinct
402
- // bucket: daily/monthly windows are telemetry-only (the counter has no
403
- // time axis), so those keep their own sums.
404
- totalTokens: templateLifetime + kpi.tokens,
399
+ // The template counter IS the running family total (live accumulator).
400
+ // Daily/monthly windows stay telemetry-only (the counter has no time
401
+ // axis), so those keep their own sums.
402
+ totalTokens: templateLifetime,
405
403
  todayTokens,
406
404
  monthTokens,
407
405
  // Lifetime counts come from the durable rollup, not the page: a family
@@ -13,7 +13,7 @@ import { apiKeys, telemetryUsageTotals } from "../../persistence/schema";
13
13
  import { shouldMaskClientIp } from "../shared/ip-privacy";
14
14
  import { utcDayStart } from "../shared/day";
15
15
 
16
- export interface SharedKeyTokenUsage {
16
+ interface SharedKeyTokenUsage {
17
17
  readonly requests: number;
18
18
  readonly errors: number;
19
19
  readonly inputTokens: number;
@@ -7,7 +7,7 @@ import { formatPublicErrorMessage } from "../../protocol/gateway-error";
7
7
  // (or the storage-layer BackupError, same code/status/message shape) and
8
8
  // shapes it through errorResponse; guards narrow to TenantAccessDecision.
9
9
 
10
- export interface ErrorResponseOptions {
10
+ interface ErrorResponseOptions {
11
11
  readonly detailsPolicy?: "omit" | "include-if-present" | "always-include";
12
12
  }
13
13
 
@@ -105,7 +105,7 @@ export function requireAnyScope(
105
105
  return access;
106
106
  }
107
107
 
108
- export type TenantAccessDecision = AccessDecision & { readonly tenantId: string };
108
+ type TenantAccessDecision = AccessDecision & { readonly tenantId: string };
109
109
 
110
110
  export function requireTenantScope(
111
111
  access: AccessDecision | undefined,
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Builds an object from `source`, omitting absent values.
3
+ *
4
+ * The console domains hand-roll the same `...(x === undefined ? {} : { x })`
5
+ * field-omission spread many times (key create/update patches, response
6
+ * sanitizers). This is the single helper for that pattern; `omitNull` extends
7
+ * it to also drop `null` values (the `== null` variant), matching call sites
8
+ * that treat null as "clear this field" rather than "set it to null".
9
+ */
10
+ export function pickDefined<T extends object>(source: T, omitNull = false): { [K in keyof T]?: Exclude<T[K], undefined> } {
11
+ const out: Record<string, unknown> = {};
12
+ for (const [key, value] of Object.entries(source)) {
13
+ if (value === undefined) continue;
14
+ if (omitNull && value === null) continue;
15
+ out[key] = value;
16
+ }
17
+ return out as { [K in keyof T]?: Exclude<T[K], undefined> };
18
+ }
@@ -31,7 +31,7 @@
31
31
  import { createHash } from "node:crypto";
32
32
 
33
33
  /** The slice of Redis this cache needs, so it can be tested without a server. */
34
- export interface ReadThroughCacheClient {
34
+ interface ReadThroughCacheClient {
35
35
  get(key: string): Promise<string | null>;
36
36
  set(key: string, value: string, mode: "EX", ttlSeconds: number): Promise<unknown>;
37
37
  }
@@ -41,7 +41,7 @@ export interface ReadThroughCacheClient {
41
41
  * more often pass a shorter TTL; this value suits the dashboard's polling
42
42
  * cadence and the append-only telemetry log.
43
43
  */
44
- export const READ_THROUGH_DEFAULT_TTL_SECONDS = 10;
44
+ const READ_THROUGH_DEFAULT_TTL_SECONDS = 10;
45
45
 
46
46
  /** The cached value plus the strong validator for the bytes it serializes to. */
47
47
  export interface CachedResult<T> {
@@ -59,7 +59,7 @@ function etagFor(bytes: string): string {
59
59
  }
60
60
 
61
61
  /** The sibling key holding the validator, so a cached body is never re-hashed. */
62
- export function etagKey(key: string): string {
62
+ function etagKey(key: string): string {
63
63
  return `${key}:etag`;
64
64
  }
65
65
 
@@ -196,7 +196,7 @@ export function conditionalResponse<T>(
196
196
  }
197
197
 
198
198
  /** True when an `If-None-Match` header names this validator. */
199
- export function matchesEtag(header: string | null, etag: string): boolean {
199
+ function matchesEtag(header: string | null, etag: string): boolean {
200
200
  if (header === null) return false;
201
201
  const trimmed = header.trim();
202
202
  if (trimmed === "*") return true;
@@ -0,0 +1,14 @@
1
+ import type { CartethyiaDatabase } from "../../persistence/db-handle";
2
+
3
+ /**
4
+ * Casts a Drizzle nested-transaction handle to the shared database type.
5
+ *
6
+ * Drizzle types a nested transaction callback with the dialect's own
7
+ * transaction handle, which differs from `CartethyiaDatabase`. The cast is
8
+ * required (both drivers implement the surface stores use), but it is a type
9
+ * hole every time it appears raw — so it is centralized here rather than
10
+ * scattered across call sites.
11
+ */
12
+ export function asConsoleDb(tx: unknown): CartethyiaDatabase {
13
+ return tx as unknown as CartethyiaDatabase;
14
+ }
@@ -16,33 +16,70 @@ export interface PromptSetting {
16
16
  export const PROMPT_MODES = ["append", "override"] as const;
17
17
  export type PromptMode = (typeof PROMPT_MODES)[number];
18
18
 
19
- /** Per-provider entry: follow the tenant's global prompt, or carry a custom one. */
20
- export interface ProviderPromptEntry {
21
- readonly followGlobal: boolean;
22
- /** Present and used only when `followGlobal` is false. */
23
- readonly setting?: PromptSetting;
19
+ /**
20
+ * A named, reusable prompt. Scopes reference presets by id instead of each
21
+ * carrying its own text, so one tone/ruleset can be applied to many scopes.
22
+ */
23
+ export interface SystemPromptPreset {
24
+ readonly id: string;
25
+ readonly name: string;
26
+ readonly enabled: boolean;
27
+ readonly mode: PromptMode;
28
+ readonly text: string;
24
29
  }
25
30
 
31
+ /**
32
+ * How one scope decides what to inject. Modes:
33
+ *
34
+ * - `off` — explicitly inject nothing for this scope; scope-specific stop,
35
+ * does NOT fall through to the global prompt.
36
+ * - `global` — follow the tenant's global prompt (the default posture).
37
+ * - `preset` — use the referenced named preset (disabled preset = no inject).
38
+ * - `custom` — carry this scope's own prompt inline.
39
+ *
40
+ * A scope with no entry at all behaves as `global` (resolution falls through
41
+ * to the global prompt), so omitting a key is the same as an explicit
42
+ * `{ kind: "global" }` reference. The global scope itself never uses
43
+ * `{ kind: "global" }` — it is `null` (off), a preset, or a custom setting.
44
+ */
45
+ export type ScopePromptRef =
46
+ | { readonly kind: "off" }
47
+ | { readonly kind: "global" }
48
+ | { readonly kind: "preset"; readonly presetId: string }
49
+ | { readonly kind: "custom"; readonly setting: PromptSetting };
50
+
26
51
  /** The full per-tenant configuration, one row in `tenant_system_prompts`. */
27
52
  export interface TenantSystemPrompts {
28
- /** `null` = never configured — distinct from an explicit disabled setting. */
29
- readonly global: PromptSetting | null;
30
- /** providerId → entry */
31
- readonly providers: Readonly<Record<string, ProviderPromptEntry>>;
32
- /** `${providerId}::${modelId}` → setting */
33
- readonly models: Readonly<Record<string, PromptSetting>>;
53
+ /** `null` = no global injection — distinct from an explicit disabled setting. */
54
+ readonly global: ScopePromptRef | null;
55
+ /** preset id → preset */
56
+ readonly presets: Readonly<Record<string, SystemPromptPreset>>;
57
+ /** providerId → ref */
58
+ readonly providers: Readonly<Record<string, ScopePromptRef>>;
59
+ /** `${providerId}::${modelId}` → ref */
60
+ readonly models: Readonly<Record<string, ScopePromptRef>>;
61
+ /** combo name → ref */
62
+ readonly combos: Readonly<Record<string, ScopePromptRef>>;
63
+ /** alias name → ref */
64
+ readonly aliases: Readonly<Record<string, ScopePromptRef>>;
34
65
  }
35
66
 
36
67
  /** Empty configuration: nothing configured anywhere. */
37
68
  export const EMPTY_TENANT_SYSTEM_PROMPTS: TenantSystemPrompts = {
38
69
  global: null,
70
+ presets: {},
39
71
  providers: {},
40
72
  models: {},
73
+ combos: {},
74
+ aliases: {},
41
75
  };
42
76
 
43
77
  /** Maximum stored text per scope (5 MB). Enforced at the console API. */
44
78
  export const SYSTEM_PROMPT_TEXT_CAP_BYTES = 5_000_000;
45
79
 
80
+ /** Maximum stored preset-name length. Enforced at the console API. */
81
+ export const SYSTEM_PROMPT_PRESET_NAME_CAP = 200;
82
+
46
83
  /**
47
84
  * Marker wrapping tenant text injected into a provider wire whose upstream
48
85
  * validates the system prompt as a channel identity (the buddy family). The
@@ -52,12 +89,21 @@ export const SYSTEM_PROMPT_TEXT_CAP_BYTES = 5_000_000;
52
89
  export const SYSTEM_PROMPT_WRAPPER_PREFIX = "<cartethyia:system-prompt>";
53
90
  export const SYSTEM_PROMPT_WRAPPER_SUFFIX = "</cartethyia:system-prompt>";
54
91
 
92
+ /** Maximum number of presets stored for one tenant. */
93
+ export const SYSTEM_PROMPT_PRESET_LIMIT = 64;
94
+
55
95
  /** Maximum number of per-provider entries stored for one tenant. */
56
96
  export const SYSTEM_PROMPT_PROVIDER_LIMIT = 256;
57
97
 
58
98
  /** Maximum number of per-model entries stored for one tenant. */
59
99
  export const SYSTEM_PROMPT_MODEL_LIMIT = 512;
60
100
 
101
+ /** Maximum number of per-combo entries stored for one tenant. */
102
+ export const SYSTEM_PROMPT_COMBO_LIMIT = 512;
103
+
104
+ /** Maximum number of per-alias entries stored for one tenant. */
105
+ export const SYSTEM_PROMPT_ALIAS_LIMIT = 512;
106
+
61
107
  /**
62
108
  * Providers whose upstream validates the fixed system prompt as a channel
63
109
  * identity. Their system turn must keep the fixed prompt as the opener, so
@@ -66,7 +112,7 @@ export const SYSTEM_PROMPT_MODEL_LIMIT = 512;
66
112
  * `override` mode is not honoured for these providers — it would strip the
67
113
  * identity and the upstream would refuse the request.
68
114
  */
69
- export const WRAPPED_INJECTION_PROVIDERS: ReadonlySet<string> = new Set([
115
+ const WRAPPED_INJECTION_PROVIDERS: ReadonlySet<string> = new Set([
70
116
  "cb", // CodeBuddy International
71
117
  "cbcn", // CodeBuddy CN
72
118
  "workbuddy",
@@ -100,3 +146,34 @@ export function isPromptSetting(value: unknown): value is PromptSetting {
100
146
  typeof candidate.text === "string"
101
147
  );
102
148
  }
149
+
150
+ /** True when the value is a well-formed preset (shape only, not size). */
151
+ export function isSystemPromptPreset(value: unknown): value is SystemPromptPreset {
152
+ if (typeof value !== "object" || value === null) return false;
153
+ const candidate = value as Partial<SystemPromptPreset>;
154
+ return (
155
+ typeof candidate.id === "string" &&
156
+ candidate.id.length > 0 &&
157
+ typeof candidate.name === "string" &&
158
+ candidate.name.length > 0 &&
159
+ typeof candidate.enabled === "boolean" &&
160
+ typeof candidate.mode === "string" &&
161
+ (PROMPT_MODES as readonly string[]).includes(candidate.mode) &&
162
+ typeof candidate.text === "string"
163
+ );
164
+ }
165
+
166
+ /** True when the value is a well-formed scope reference (shape only). */
167
+ export function isScopePromptRef(value: unknown): value is ScopePromptRef {
168
+ if (typeof value !== "object" || value === null) return false;
169
+ const kind = (value as { kind?: unknown }).kind;
170
+ if (kind === "off" || kind === "global") return true;
171
+ if (kind === "preset") {
172
+ const presetId = (value as { presetId?: unknown }).presetId;
173
+ return typeof presetId === "string" && presetId.length > 0;
174
+ }
175
+ if (kind === "custom") {
176
+ return isPromptSetting((value as { setting?: unknown }).setting);
177
+ }
178
+ return false;
179
+ }