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
@@ -66,13 +66,13 @@ const MAX_FILES_PER_RUN = 64;
66
66
  */
67
67
  const HOURS_TO_SKIP = 2;
68
68
 
69
- export interface ReconcileOptions {
69
+ interface ReconcileOptions {
70
70
  /** Report only: count the gap without writing anything. */
71
71
  readonly verify?: boolean;
72
72
  readonly now?: Date;
73
73
  }
74
74
 
75
- export interface ReconcileReport {
75
+ interface ReconcileReport {
76
76
  readonly ran: boolean;
77
77
  /** Why the run did nothing, when it did nothing. */
78
78
  readonly skippedReason?: "lease_held" | "no_files" | "uptodate";
@@ -393,7 +393,7 @@ async function insertChunk(
393
393
  .set({
394
394
  fileName,
395
395
  byteOffset,
396
- filesCompleted: sql`${telemetryReconciliationState.filesCompleted} + 0`,
396
+ filesCompleted: sql`${telemetryReconciliationState.filesCompleted} + 1`,
397
397
  eventsExamined: sql`${telemetryReconciliationState.eventsExamined} + ${events.length}`,
398
398
  eventsInserted: sql`${telemetryReconciliationState.eventsInserted} + ${rows.length}`,
399
399
  conflicts: sql`${telemetryReconciliationState.conflicts} + ${events.length - rows.length}`,
@@ -31,7 +31,7 @@
31
31
  * but those spans measure local response processing, not decode. Only a
32
32
  * client-visible stream window counts as decode time.
33
33
  */
34
- export interface TokenSpeedInput {
34
+ interface TokenSpeedInput {
35
35
  /** Completion/output tokens reported for the request. */
36
36
  readonly outputTokens?: number | undefined;
37
37
  /** End-to-end gateway latency in milliseconds. */
@@ -23,7 +23,7 @@ export interface ConnectionUrlCandidate {
23
23
  }
24
24
 
25
25
  /** How one backing service's connection string is resolved. */
26
- export interface ConnectionUrlSpec {
26
+ interface ConnectionUrlSpec {
27
27
  /** Accepted URL schemes, e.g. `postgres:` / `postgresql:`. */
28
28
  readonly schemes: readonly string[];
29
29
  /** URL variables, in resolution order. */
@@ -17,7 +17,7 @@ export const fullSchema = schema;
17
17
  export type CartethyiaDatabase = NodePgDatabase<typeof fullSchema>;
18
18
 
19
19
  /** Raw rows from a parameter query, without driver-specific decoration. */
20
- export interface QueryRows {
20
+ interface QueryRows {
21
21
  readonly rows: unknown[];
22
22
  }
23
23
 
@@ -81,7 +81,7 @@ function databaseUrlFromLibpqEnv(): string | undefined {
81
81
  return `postgres://${credentials}${authorityHost}:${port}${path}`;
82
82
  }
83
83
 
84
- export function requireDatabaseUrl(): string {
84
+ function requireDatabaseUrl(): string {
85
85
  return requireConnectionUrl({
86
86
  schemes: ["postgres:", "postgresql:"],
87
87
  candidates: databaseUrlCandidates(),
@@ -124,7 +124,7 @@ export function poolIdleFloorFromEnv(): number {
124
124
  * only closes an idle client after `idleTimeoutMillis` (60s here); this gives
125
125
  * the pool a faster, explicit shrink path while still keeping the floor warm.
126
126
  */
127
- export function startPoolShrinker(pool: Pool, floor: number): () => void {
127
+ function startPoolShrinker(pool: Pool, floor: number): () => void {
128
128
  const interval = setInterval(() => {
129
129
  try {
130
130
  const idle = pool.idleCount;
@@ -326,7 +326,7 @@ const AUTH_KEY_COLUMNS = [
326
326
  "id", "tenant_id", "key_hash", "key_mode", "parent_key_id", "issued_client_ip",
327
327
  "issued_client_ip_key", "label", "scopes", "requests_per_minute", "daily_token_limit",
328
328
  "monthly_token_limit", "lifetime_token_budget", "lifetime_tokens_consumed",
329
- "max_concurrent_requests", "model_access_mode", "model_list", "client_router_denylist",
329
+ "max_concurrent_requests", "concurrency_scope", "model_access_mode", "model_list", "client_router_denylist",
330
330
  "model_prefix", "created_at", "enabled", "revoked_at", "banned_at", "ban_reason", "banned_until",
331
331
  "key_prefix", "key_encrypted",
332
332
  "notes_title", "notes_subtitle", "notes_body", "is_default", "sort_index",
@@ -341,7 +341,7 @@ export interface SqliteAuthStatements {
341
341
  readonly childrenConsumed: { get: (parentId: string) => { total: number } | null };
342
342
  }
343
343
 
344
- export function prepareAuthStatements(sqlite: Database): SqliteAuthStatements {
344
+ function prepareAuthStatements(sqlite: Database): SqliteAuthStatements {
345
345
  // A key is active while not revoked and not under an active ban: a ban is
346
346
  // active when `banned_at` is set and `banned_until` is null (permanent) or
347
347
  // still in the future. SQLite stores timestamps as ISO text, so the
@@ -507,7 +507,7 @@ function applyFilePragmas(db: Database): void {
507
507
  }
508
508
 
509
509
  /** The SQLite set lives beside the PostgreSQL one, never inside it. */
510
- export function resolveSqliteMigrationsFolder(): string {
510
+ function resolveSqliteMigrationsFolder(): string {
511
511
  return resolvePackagePath("migrations", "lite-sqlite3");
512
512
  }
513
513
 
@@ -143,7 +143,11 @@ export function affectedRows(result: unknown): number {
143
143
  * `.for()`, but a transaction there is already the write lock, so the query
144
144
  * runs as-is. The two builders disagree on `.for()`, so the call is typed at
145
145
  * the boundary instead of pushed into every store.
146
+ *
147
+ * The dialect is read from the query itself, not the process environment: a
148
+ * test may build a SQLite handle while the suite runs in full mode, and a
149
+ * PostgreSQL query always carries `.for` while a SQLite one never does.
146
150
  */
147
151
  export function lockRows<T>(query: T & { for?(mode: string): PromiseLike<unknown> }): PromiseLike<T extends PromiseLike<infer R> ? R : never> {
148
- return (sqliteDialect() ? query : query.for!("update")) as PromiseLike<T extends PromiseLike<infer R> ? R : never>;
152
+ return (typeof query.for === "function" ? query.for!("update") : query) as PromiseLike<T extends PromiseLike<infer R> ? R : never>;
149
153
  }
@@ -45,7 +45,7 @@ function sameMemoKey(left: ReadinessMemoKey, right: ReadinessMemoKey): boolean {
45
45
  * Returns typed ready/not_ready response.
46
46
  */
47
47
 
48
- export type ReadinessStatus = "ready" | "not_ready";
48
+ type ReadinessStatus = "ready" | "not_ready";
49
49
 
50
50
  export interface ReadinessCheckResult {
51
51
  readonly status: ReadinessStatus;
@@ -92,7 +92,7 @@ export function resolveRedisBackend(client: RedisClient | undefined): RedisBacke
92
92
  return client === undefined ? "memory" : "redis";
93
93
  }
94
94
 
95
- export interface CloseRedisOptions {
95
+ interface CloseRedisOptions {
96
96
  /** Bounded wait for `QUIT` before falling back to `disconnect()`. Defaults to 2s. */
97
97
  readonly quitTimeoutMs?: number;
98
98
  }
@@ -4,9 +4,23 @@ import {
4
4
  bigint, boolean, check, customType, index, integer, jsonb, numeric, pgEnum, pgTable,
5
5
  primaryKey, text, timestamp, unique, uniqueIndex, uuid, type AnyPgColumn,
6
6
  } from "drizzle-orm/pg-core";
7
+ import type { ProviderQuotaResult } from "../providers/quota/quota-contracts";
7
8
 
8
- export const TENANT_STATUSES = ["active", "suspended", "deleted"] as const;
9
- export type TenantStatus = (typeof TENANT_STATUSES)[number];
9
+ /**
10
+ * The persisted copy of one account's last successful quota read.
11
+ *
12
+ * Survives process restarts, which the in-process quota cache does not — so a
13
+ * cold page open can render the last known balance immediately while the sweep
14
+ * refreshes it. Stored only on success; a failed fetch leaves the previous
15
+ * snapshot in place.
16
+ */
17
+ export interface ProviderQuotaResultSnapshot {
18
+ readonly quota: ProviderQuotaResult;
19
+ readonly fetchedAt: string;
20
+ }
21
+
22
+ const TENANT_STATUSES = ["active", "suspended", "deleted"] as const;
23
+ type TenantStatus = (typeof TENANT_STATUSES)[number];
10
24
 
11
25
  export const tenants = pgTable(
12
26
  "tenants",
@@ -26,35 +40,35 @@ export const tenants = pgTable(
26
40
 
27
41
  // Canonical tenant/timestamp column definitions shared by every table.
28
42
 
29
- export function tenantRefNullable() {
43
+ function tenantRefNullable() {
30
44
  return uuid("tenant_id").references(() => tenants.id, { onDelete: "cascade" });
31
45
  }
32
46
 
33
- export function tenantRefRequired() {
47
+ function tenantRefRequired() {
34
48
  return uuid("tenant_id")
35
49
  .notNull()
36
50
  .references(() => tenants.id, { onDelete: "cascade" });
37
51
  }
38
52
 
39
- export function tenantRefPrimaryKey() {
53
+ function tenantRefPrimaryKey() {
40
54
  return uuid("tenant_id")
41
55
  .primaryKey()
42
56
  .references(() => tenants.id, { onDelete: "cascade" });
43
57
  }
44
58
 
45
- export function tenantCascadeSetNull() {
59
+ function tenantCascadeSetNull() {
46
60
  return uuid("tenant_id").references(() => tenants.id, { onDelete: "set null" });
47
61
  }
48
62
 
49
- export function createdAtColumn() {
63
+ function createdAtColumn() {
50
64
  return timestamp("created_at", { withTimezone: true }).notNull().defaultNow();
51
65
  }
52
66
 
53
- export function updatedAtColumn() {
67
+ function updatedAtColumn() {
54
68
  return timestamp("updated_at", { withTimezone: true }).notNull().defaultNow();
55
69
  }
56
70
 
57
- export function timestampColumns() {
71
+ function timestampColumns() {
58
72
  return { createdAt: createdAtColumn(), updatedAt: updatedAtColumn() };
59
73
  }
60
74
 
@@ -90,7 +104,7 @@ export const healthStatus = pgEnum("health_status", ["active", "cooldown", "disa
90
104
  * One definition, because the two tables are driven by the same transitions and
91
105
  * a column added to one of them by hand is how they drift.
92
106
  */
93
- export function healthColumns() {
107
+ function healthColumns() {
94
108
  return {
95
109
  status: healthStatus("status").notNull().default("active"),
96
110
  consecutiveFailures: integer("consecutive_failures").notNull().default(0),
@@ -295,6 +309,18 @@ export const providerAccounts = pgTable("provider_accounts", {
295
309
  * to an account that may well be refused.
296
310
  */
297
311
  quotaPlan: text("quota_plan"),
312
+ /**
313
+ * The last successful quota read for this account, as a JSON snapshot.
314
+ *
315
+ * Persisted rather than left only in the quota cache: that cache is a short
316
+ * in-process TTL and empty on a cold start, so right after a restart the
317
+ * console would show no balance for every account until the sweep re-read
318
+ * them. The row survives the restart, so a cold page open renders the last
319
+ * known numbers immediately (and the sweep refreshes them). Only a
320
+ * *successful* read is stored — a failed fetch leaves the previous snapshot
321
+ * in place, mirroring the plan column's behaviour.
322
+ */
323
+ quotaSnapshot: jsonb("quota_snapshot").$type<ProviderQuotaResultSnapshot | null>(),
298
324
 
299
325
  /**
300
326
  * Qoder native machine identity bound to this account, as one JSON object
@@ -342,7 +368,6 @@ export const providerAccounts = pgTable("provider_accounts", {
342
368
  ],
343
369
  );
344
370
 
345
-
346
371
  // OAuth refresh token + cross-process lease, normalized out of
347
372
  // `provider_accounts` into its own 1:0/1 table. The vast majority of
348
373
  // provider accounts are `credentialKind: "api_key"`, for which these four
@@ -381,7 +406,6 @@ export const providerOauthStates = pgTable("provider_oauth_states", {
381
406
  leaseExpiresAt: timestamp("lease_expires_at", { withTimezone: true }),
382
407
  });
383
408
 
384
-
385
409
  // One row per (provider_id, model_id, route), with `endpoint_path`
386
410
  // identifying the route — the same model through two providers, or through
387
411
  // two routes of one provider, is two rows. `enabled` is a hard routing
@@ -423,7 +447,7 @@ export const models = pgTable(
423
447
  * that predicts access — `credits` and `vendor` were both measured against
424
448
  * real dispatches and neither correlates. Only the refusal itself is a
425
449
  * signal, so the answer has to be recorded. See
426
- * `.skills/cartethyia-reverse-engineering/references/codebuddy-dispatch-headers.md`.
450
+ * `documentation/research/codebuddy/2026-10-19-codebuddy-chat-dispatch-headers.md`.
427
451
  */
428
452
  proOnly: boolean("pro_only"),
429
453
  /**
@@ -435,6 +459,12 @@ export const models = pgTable(
435
459
  * provider did not state it, which is not the same as billed.
436
460
  */
437
461
  freeTier: boolean("free_tier"),
462
+ /**
463
+ * Explicit wire dialect for this row (nullable). Absent = resolver
464
+ * fallback (heuristic + family default). See
465
+ * `src/protocol/translation/dialect/`.
466
+ */
467
+ dialect: text("dialect"),
438
468
  },
439
469
  (table) => [
440
470
  uniqueIndex("models_provider_model_route_uidx").on(
@@ -476,7 +506,6 @@ export const tenantDisabledModels = pgTable(
476
506
  ],
477
507
  );
478
508
 
479
-
480
509
  // Tenant-owned HTTP/HTTPS/SOCKS5 pools share the provider-account health state machine.
481
510
  export const networkPools = pgTable("network_pools", {
482
511
  id: uuid("id").primaryKey().defaultRandom(),
@@ -611,8 +640,6 @@ export const providerPoolAccounts = pgTable(
611
640
  index("provider_pool_accounts_provider_status_idx").on(table.providerId, table.status),
612
641
  ],
613
642
  );
614
- export type ProviderPoolAccount = typeof providerPoolAccounts.$inferSelect;
615
-
616
643
 
617
644
  // Account and network-pool health events share one table. The entity kind
618
645
  // selects exactly one owner FK, and the CHECK constraint below preserves
@@ -653,7 +680,6 @@ export const healthEvents = pgTable(
653
680
  ],
654
681
  );
655
682
 
656
-
657
683
  // Model aliasing & combos. Aliases resolve a client-facing name to a
658
684
  // real model; combos back a client-facing name with >=1 real models,
659
685
  // auto-selected via `fallback` or `round_robin`.
@@ -806,7 +832,6 @@ export const poolRoutingSettings = pgTable("pool_routing_settings", {
806
832
  rotateCount: integer("rotate_count").notNull().default(1),
807
833
  });
808
834
 
809
-
810
835
  export const API_KEY_MODES = ["personal", "share"] as const;
811
836
  export type ApiKeyMode = (typeof API_KEY_MODES)[number];
812
837
 
@@ -820,6 +845,10 @@ export type ApiKeyMode = (typeof API_KEY_MODES)[number];
820
845
  export const API_KEY_MODEL_ACCESS_MODES = ["whitelist", "blacklist"] as const;
821
846
  export type ApiKeyModelAccessMode = (typeof API_KEY_MODEL_ACCESS_MODES)[number];
822
847
 
848
+ /** Share-template concurrency scope — see `apiKeys.concurrencyScope`. */
849
+ export const API_KEY_CONCURRENCY_SCOPES = ["family", "per_key"] as const;
850
+ export type ApiKeyConcurrencyScope = (typeof API_KEY_CONCURRENCY_SCOPES)[number];
851
+
823
852
  /**
824
853
  * Inbound `/v1/*` keys never store plaintext secrets. Personal keys carry an
825
854
  * authentication hash; share templates carry policy and child keys point back
@@ -858,6 +887,24 @@ export const apiKeys = pgTable(
858
887
  .notNull()
859
888
  .default(0),
860
889
  maxConcurrentRequests: integer("max_concurrent_requests"),
890
+ /**
891
+ * How `maxConcurrentRequests` applies to a share family:
892
+ *
893
+ * - `family` (default) — one cap across the whole family: every recipient
894
+ * key shares the same admission counter and budget, so 20 means 20
895
+ * concurrent requests across all children of the template.
896
+ * - `per_key` — each recipient key gets its own independent concurrent
897
+ * window and cap. The cap is copied from the template to each child at
898
+ * issue time (children carry their own `maxConcurrentRequests`), and a
899
+ * child's admission identity is its own id, not the parent's.
900
+ *
901
+ * Only meaningful for `share` keys; personal keys always use their own
902
+ * per-key cap.
903
+ */
904
+ concurrencyScope: text("concurrency_scope")
905
+ .$type<ApiKeyConcurrencyScope>()
906
+ .notNull()
907
+ .default("family"),
861
908
  /**
862
909
  * Slot cap for a share template: the maximum number of concurrently
863
910
  * active child keys it may mint. `null` = unlimited (legacy behavior).
@@ -984,7 +1031,6 @@ export const sharePopups = pgTable(
984
1031
  ],
985
1032
  );
986
1033
 
987
-
988
1034
  // Public bearer links for enrolling one shared key per resolved client IP, and
989
1035
  // for handing a personal key to its owner. The hash is the lookup key; the
990
1036
  // token is additionally retained encrypted so the owner's console can show the
@@ -993,12 +1039,11 @@ export const sharePopups = pgTable(
993
1039
  * Share-link kinds, as a runtime tuple. Route schemas and DTOs project this
994
1040
  * list rather than restating it.
995
1041
  */
996
- export const SHARE_LINK_KINDS = ["enroll", "handoff"] as const;
1042
+ const SHARE_LINK_KINDS = ["enroll", "handoff"] as const;
997
1043
 
998
1044
  /** One share-link kind. */
999
1045
  export type ShareLinkKind = (typeof SHARE_LINK_KINDS)[number];
1000
1046
 
1001
-
1002
1047
  export const shareLinks = pgTable(
1003
1048
  "share_links",
1004
1049
  {
@@ -1040,7 +1085,6 @@ export const shareLinks = pgTable(
1040
1085
  ],
1041
1086
  );
1042
1087
 
1043
-
1044
1088
  /**
1045
1089
  * Console dashboard users — distinct from API-key tenants.
1046
1090
  */
@@ -1113,7 +1157,6 @@ export const consoleLockouts = pgTable(
1113
1157
  ],
1114
1158
  );
1115
1159
 
1116
-
1117
1160
  // Configuration-change audit has its own retention lifecycle; it is separate
1118
1161
  // from metadata telemetry and short-lived optional payload capture.
1119
1162
  export const adminAuditLog = pgTable(
@@ -1136,7 +1179,6 @@ export const adminAuditLog = pgTable(
1136
1179
  ],
1137
1180
  );
1138
1181
 
1139
-
1140
1182
  // Runtime preferences are stored in one validated JSONB bag. The filter
1141
1183
  // master toggle remains a typed column because it is read on the proxy path.
1142
1184
  export interface ConsoleSettingsPreferences {
@@ -1232,6 +1274,14 @@ export interface ConsoleSettingsPreferences {
1232
1274
  * the safer direction and the more common need.
1233
1275
  */
1234
1276
  imageClampMaxBytes?: number | null;
1277
+ /**
1278
+ * Ordered list of vision model ids (`provider/model`) used to describe
1279
+ * images when the selected model is not image-capable. The first healthy
1280
+ * model wins; if it fails (cooldown/quota/upstream error), the handoff
1281
+ * falls through to the next entry. Empty/absent = legacy `[image]`
1282
+ * placeholder behavior.
1283
+ */
1284
+ visionHandoffModels?: readonly string[];
1235
1285
  }
1236
1286
 
1237
1287
  export const consoleSettings = pgTable("console_settings", {
@@ -1242,21 +1292,33 @@ export const consoleSettings = pgTable("console_settings", {
1242
1292
 
1243
1293
  // Tenant-scoped system prompt injection (append/override). One row per
1244
1294
  // tenant; `global` NULL means never configured, distinct from an explicit
1245
- // disabled setting. Providers/models are JSON object maps keyed by
1246
- // providerId and `providerId::modelId` respectively — see
1247
- // `src/console/system-prompt/contracts.ts` for the value shapes and the
1248
- // resolution rules implemented by the dispatch-path transform.
1295
+ // disabled setting. Providers/models/combos/aliases are JSON object maps
1296
+ // keyed by providerId, `providerId::modelId`, combo name, and alias name
1297
+ // respectively — see `src/console/system-prompt/contracts.ts` for the value
1298
+ // shapes and the resolution rules implemented by the dispatch-path transform.
1249
1299
  export const tenantSystemPrompts = pgTable("tenant_system_prompts", {
1250
1300
  tenantId: tenantRefPrimaryKey(),
1251
- global: jsonb("global").$type<import("../console/system-prompt/contracts").PromptSetting | null>(),
1301
+ global: jsonb("global").$type<import("../console/system-prompt/contracts").ScopePromptRef | null>(),
1302
+ presets: jsonb("presets")
1303
+ .notNull()
1304
+ .default({})
1305
+ .$type<Record<string, import("../console/system-prompt/contracts").SystemPromptPreset>>(),
1252
1306
  providers: jsonb("providers")
1253
1307
  .notNull()
1254
1308
  .default({})
1255
- .$type<Record<string, import("../console/system-prompt/contracts").ProviderPromptEntry>>(),
1309
+ .$type<Record<string, import("../console/system-prompt/contracts").ScopePromptRef>>(),
1256
1310
  models: jsonb("models")
1257
1311
  .notNull()
1258
1312
  .default({})
1259
- .$type<Record<string, import("../console/system-prompt/contracts").PromptSetting>>(),
1313
+ .$type<Record<string, import("../console/system-prompt/contracts").ScopePromptRef>>(),
1314
+ combos: jsonb("combos")
1315
+ .notNull()
1316
+ .default({})
1317
+ .$type<Record<string, import("../console/system-prompt/contracts").ScopePromptRef>>(),
1318
+ aliases: jsonb("aliases")
1319
+ .notNull()
1320
+ .default({})
1321
+ .$type<Record<string, import("../console/system-prompt/contracts").ScopePromptRef>>(),
1260
1322
  updatedAt: updatedAtColumn(),
1261
1323
  });
1262
1324
 
@@ -1276,7 +1338,6 @@ export const inferhubConfigKeys = pgTable("inferhub_config_keys", {
1276
1338
  updatedAt: updatedAtColumn(),
1277
1339
  });
1278
1340
 
1279
-
1280
1341
  // CLI Tools (CLI tool configuration): per-tenant mapping table that lets a coding-agent
1281
1342
  // slot (e.g. Claude Code's "opus" slot) target a specific Cartethyia model. Rows
1282
1343
  // are opaque strings — validation happens at the API boundary against the
@@ -1306,7 +1367,6 @@ export const cliToolMappings = pgTable(
1306
1367
  ],
1307
1368
  );
1308
1369
 
1309
-
1310
1370
  // Per-(tenant, tool, api_key) settings: whether mappings are active and which
1311
1371
  // mode the tool is configured for. Primary key is the composite of all three.
1312
1372
  export const cliToolSettings = pgTable(
@@ -1324,7 +1384,6 @@ export const cliToolSettings = pgTable(
1324
1384
  (table) => [primaryKey({ columns: [table.tenantId, table.toolId, table.apiKeyId] })],
1325
1385
  );
1326
1386
 
1327
-
1328
1387
  // Metadata-only request telemetry. Retention is configured independently of
1329
1388
  // payload capture (`CARTETHYIA_TELEMETRY_RETENTION_DAYS`, default 30 days).
1330
1389
  // No prompt text, raw body, API key, encrypted-reasoning payload, or generic
@@ -1424,6 +1483,14 @@ export const telemetryEvents = pgTable(
1424
1483
  * tell "not configured" from "broken".
1425
1484
  */
1426
1485
  payloadCaptureState: text("payload_capture_state"),
1486
+ /**
1487
+ * Set for a completed request whose output is far smaller than what the
1488
+ * model can produce AND which the model ended with an ordinary `stop`
1489
+ * (a "degenerate stop"). Introduced to measure how often a model
1490
+ * truncates itself on a cached prefix — the pattern that surfaced as
1491
+ * short, cut-off chat replies. NULL/unset for everything else.
1492
+ */
1493
+ degenerateStop: boolean("degenerate_stop"),
1427
1494
  },
1428
1495
  (table) => [
1429
1496
  index("telemetry_events_created_at_idx").on(table.createdAt),
@@ -1473,6 +1540,22 @@ export const telemetryEvents = pgTable(
1473
1540
  table.accountId,
1474
1541
  table.createdAt,
1475
1542
  ),
1543
+ // Provider usage matrix + `breakdownTelemetry("provider")`: equality on
1544
+ // `provider_id`, range+aggregate on `created_at`. Was served by the
1545
+ // tenant-range index, which scanned every event in the window for one
1546
+ // provider's card.
1547
+ index("telemetry_events_tenant_provider_created_idx").on(
1548
+ table.tenantId,
1549
+ table.providerId,
1550
+ table.createdAt,
1551
+ ),
1552
+ // `breakdownTelemetry("model")` (top-model lists): equality on
1553
+ // `requested_model`, range+aggregate on `created_at`.
1554
+ index("telemetry_events_tenant_model_created_idx").on(
1555
+ table.tenantId,
1556
+ table.requestedModel,
1557
+ table.createdAt,
1558
+ ),
1476
1559
  /**
1477
1560
  * Serves the public share page's per-key aggregate (`share-usage.ts`):
1478
1561
  * equality on `api_key_id`, range and aggregate on `created_at`. Without
@@ -1514,7 +1597,7 @@ export const telemetryReconciliationState = pgTable(
1514
1597
  );
1515
1598
 
1516
1599
  /** Durable aggregates retained after metadata telemetry expires. */
1517
- export const TELEMETRY_USAGE_IDENTITY_TYPES = ["account", "api_key"] as const;
1600
+ const TELEMETRY_USAGE_IDENTITY_TYPES = ["account", "api_key"] as const;
1518
1601
  export type TelemetryUsageIdentityType = (typeof TELEMETRY_USAGE_IDENTITY_TYPES)[number];
1519
1602
 
1520
1603
  export const telemetryUsageTotals = pgTable(
@@ -1553,7 +1636,6 @@ export const telemetryUsageTotals = pgTable(
1553
1636
  ],
1554
1637
  );
1555
1638
 
1556
-
1557
1639
  export const telemetryPayloads = pgTable(
1558
1640
  "telemetry_payloads",
1559
1641
  {
@@ -1596,8 +1678,6 @@ export const telemetryPayloads = pgTable(
1596
1678
  ],
1597
1679
  );
1598
1680
 
1599
-
1600
-
1601
1681
  // Daily check-in ledger: one row per (account, calendar day) once the sweep has
1602
1682
  // attempted that account's claim.
1603
1683
  //
@@ -1610,7 +1690,9 @@ export const dailyCheckinLedger = pgTable(
1610
1690
  "daily_checkin_ledger",
1611
1691
  {
1612
1692
  dayKey: text("day_key").notNull(),
1613
- accountId: uuid("account_id").notNull(),
1693
+ accountId: uuid("account_id")
1694
+ .notNull()
1695
+ .references(() => providerAccounts.id, { onDelete: "cascade" }),
1614
1696
  claimedAt: timestamp("claimed_at", { withTimezone: true }).notNull().defaultNow(),
1615
1697
  },
1616
1698
  (table) => [
@@ -1618,7 +1700,6 @@ export const dailyCheckinLedger = pgTable(
1618
1700
  ],
1619
1701
  );
1620
1702
 
1621
-
1622
1703
  /**
1623
1704
  * Per-day, per-model credit deductions the buddy billing meter reported.
1624
1705
  *
@@ -33,7 +33,7 @@ const INVALIDATION_CHANNEL = "cartethyia:cache:invalidate";
33
33
  * collide, and so a `FLUSHDB`-free deploy can drop one namespace without
34
34
  * touching another.
35
35
  */
36
- export type SharedCacheNamespace = "auth" | "preferences" | "credential" | "routing" | "public-models";
36
+ type SharedCacheNamespace = "auth" | "preferences" | "credential" | "routing" | "public-models";
37
37
 
38
38
  const NAMESPACE_PREFIX: Readonly<Record<SharedCacheNamespace, string>> = {
39
39
  auth: "cartethyia:cache:auth:",
@@ -50,7 +50,7 @@ const NAMESPACE_PREFIX: Readonly<Record<SharedCacheNamespace, string>> = {
50
50
  };
51
51
 
52
52
  /** One notice that peers must drop an entry. */
53
- export interface InvalidationNotice {
53
+ interface InvalidationNotice {
54
54
  readonly namespace: SharedCacheNamespace;
55
55
  /** Omitted means "all keys in the namespace". */
56
56
  readonly key?: string;
@@ -27,7 +27,7 @@ export type SortOrderDb =
27
27
  * `idColumn`/`sortIndexColumn`, and every caller passes its own schema-typed
28
28
  * table plus the two columns.
29
29
  */
30
- export interface SortOrderTable {
30
+ interface SortOrderTable {
31
31
  readonly table: PgTable;
32
32
  readonly idColumn: AnyColumn;
33
33
  readonly sortIndexColumn: AnyColumn;
@@ -1,11 +1,11 @@
1
1
  // Persisted API-key store: the console API-key domain and gateway authentication both consume this boundary.
2
2
 
3
- import { and, asc, count, desc, eq, inArray, isNotNull, isNull, lte, or, sql, type SQL } from "drizzle-orm";
3
+ import { and, asc, count, desc, eq, gt, inArray, isNotNull, isNull, lte, or, sql, type SQL } from "drizzle-orm";
4
4
  import { nextSortIndex, reorderSortIndex, type SortOrderDb } from "../sort-order";
5
5
  import { getDbHandle } from "../boot";
6
6
  import type { CartethyiaDatabase } from "../db-handle";
7
7
  import { lockRows } from "../dialect";
8
- import { apiKeys, shareLinks, sharePopups, telemetryUsageTotals, type ApiKeyMode, type ApiKeyModelAccessMode } from "../schema";
8
+ import { apiKeys, shareLinks, sharePopups, telemetryUsageTotals, type ApiKeyMode, type ApiKeyModelAccessMode, type ApiKeyConcurrencyScope } from "../schema";
9
9
  import type { AccessScope } from "../../security/access-control";
10
10
 
11
11
  /** Sortable columns for the paged share-child listing (api_keys columns only). */
@@ -78,6 +78,7 @@ export interface ApiKeyRecord {
78
78
  readonly monthlyTokenLimit?: number;
79
79
  readonly lifetimeTokenBudget?: number;
80
80
  readonly maxConcurrentRequests?: number;
81
+ readonly concurrencyScope?: ApiKeyConcurrencyScope;
81
82
  /** Share-template recipient slot cap; undefined = unlimited. */
82
83
  readonly maxChildren?: number;
83
84
  readonly modelPrefix?: string;
@@ -252,6 +253,8 @@ async function writeSharePopup(
252
253
  /** Explicit persistence boundary. Production must provide this; there is no fallback store. */
253
254
  export interface ApiKeyStore {
254
255
  list(tenantId: string): Promise<readonly ApiKeyRecord[]>;
256
+ /** Active, non-revoked bans for one tenant, including share children. */
257
+ listActiveBanned(tenantId: string): Promise<readonly ApiKeyRecord[]>;
255
258
  get(tenantId: string, keyId: string): Promise<ApiKeyRecord | undefined>;
256
259
  listChildren(tenantId: string, parentKeyId: string): Promise<readonly ApiKeyRecord[]>;
257
260
  revokeChildrenByScope(
@@ -344,6 +347,9 @@ export class DrizzleApiKeyStore implements ApiKeyStore {
344
347
  ...(row.maxConcurrentRequests === null
345
348
  ? {}
346
349
  : { maxConcurrentRequests: row.maxConcurrentRequests }),
350
+ ...(row.concurrencyScope
351
+ ? { concurrencyScope: row.concurrencyScope as ApiKeyConcurrencyScope }
352
+ : {}),
347
353
  ...(row.maxChildren === null ? {} : { maxChildren: row.maxChildren }),
348
354
  ...(row.modelPrefix === null ? {} : { modelPrefix: row.modelPrefix }),
349
355
  modelAccessMode: row.modelAccessMode,
@@ -378,6 +384,22 @@ export class DrizzleApiKeyStore implements ApiKeyStore {
378
384
  .limit(1000);
379
385
  return this.withPopups(this.db, rows);
380
386
  }
387
+ async listActiveBanned(tenantId: string): Promise<readonly ApiKeyRecord[]> {
388
+ const now = new Date();
389
+ const rows = await this.db
390
+ .select()
391
+ .from(apiKeys)
392
+ .where(
393
+ and(
394
+ eq(apiKeys.tenantId, tenantId),
395
+ isNotNull(apiKeys.bannedAt),
396
+ isNull(apiKeys.revokedAt),
397
+ or(isNull(apiKeys.bannedUntil), gt(apiKeys.bannedUntil, now)),
398
+ ),
399
+ )
400
+ .orderBy(desc(apiKeys.bannedAt), apiKeys.id);
401
+ return rows.map((row) => this.map(row));
402
+ }
381
403
 
382
404
  /** Rewrites the list order for one tenant; `keyIds` is the full order. */
383
405
  async reorder(tenantId: string, keyIds: readonly string[]): Promise<void> {
@@ -547,6 +569,7 @@ export class DrizzleApiKeyStore implements ApiKeyStore {
547
569
  monthlyTokenLimit: record.monthlyTokenLimit ?? null,
548
570
  lifetimeTokenBudget: record.lifetimeTokenBudget ?? null,
549
571
  maxConcurrentRequests: record.maxConcurrentRequests ?? null,
572
+ concurrencyScope: record.concurrencyScope ?? "family",
550
573
  maxChildren: record.maxChildren ?? null,
551
574
  modelPrefix: record.modelPrefix ?? null,
552
575
  modelAccessMode: record.modelAccessMode ?? "whitelist",
@@ -615,6 +638,9 @@ export class DrizzleApiKeyStore implements ApiKeyStore {
615
638
  ...(patch.maxConcurrentRequests !== undefined
616
639
  ? { maxConcurrentRequests: patch.maxConcurrentRequests }
617
640
  : {}),
641
+ ...(patch.concurrencyScope !== undefined
642
+ ? { concurrencyScope: patch.concurrencyScope }
643
+ : {}),
618
644
  ...(patch.maxChildren !== undefined ? { maxChildren: patch.maxChildren } : {}),
619
645
  ...(patch.modelPrefix !== undefined ? { modelPrefix: patch.modelPrefix } : {}),
620
646
  ...(patch.modelAccessMode !== undefined ? { modelAccessMode: patch.modelAccessMode } : {}),
@@ -802,6 +828,7 @@ export class DrizzleApiKeyStore implements ApiKeyStore {
802
828
  lifetimeTokenBudget: integer("lifetime_token_budget"),
803
829
  lifetimeTokensConsumed: integer("lifetime_tokens_consumed") ?? 0,
804
830
  maxConcurrentRequests: integer("max_concurrent_requests"),
831
+ concurrencyScope: (text("concurrency_scope") ?? "family") as ApiKeyConcurrencyScope,
805
832
  modelAccessMode: (text("model_access_mode") ?? "whitelist") as ApiKeyModelAccessMode,
806
833
  modelList: json("model_list"),
807
834
  clientRouterDenylist: json("client_router_denylist"),