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
@@ -18,6 +18,7 @@
18
18
  * - `isGatewayError()` semantics live in one shared SQL predicate.
19
19
  */
20
20
  import { and, desc, eq, gte, inArray, isNotNull, or, sql, type SQL } from "drizzle-orm";
21
+ import { PgDialect } from "drizzle-orm/pg-core";
21
22
  import type { CartethyiaDatabase } from "../persistence/db-handle";
22
23
  import { apiKeys, telemetryEvents, telemetryUsageTotals } from "../persistence/schema";
23
24
  import type { TelemetryEventRecord } from "./event-log";
@@ -33,6 +34,20 @@ import { gatewayErrorSql } from "./telemetry-status";
33
34
  */
34
35
  export const IS_GATEWAY_ERROR_SQL = gatewayErrorSql(telemetryEvents.status, telemetryEvents.httpStatus);
35
36
 
37
+ /**
38
+ * The aggregate select fragments shared by every tenant-window read. The
39
+ * `errors` count uses the single shared gateway-error predicate (see
40
+ * {@link IS_GATEWAY_ERROR_SQL}) so the row-level rule and the SQL aggregate
41
+ * cannot drift; the sum fragments coalesce to 0 so an empty window reads as
42
+ * zero rather than null.
43
+ */
44
+ const AGGREGATE_SELECT = {
45
+ errors: sql<number>`sum(case when ${IS_GATEWAY_ERROR_SQL} then 1 else 0 end)`,
46
+ inputTokens: sql<number>`coalesce(sum(${telemetryEvents.inputTokens}), 0)`,
47
+ outputTokens: sql<number>`coalesce(sum(${telemetryEvents.outputTokens}), 0)`,
48
+ cachedInputTokens: sql<number>`coalesce(sum(${telemetryEvents.cachedInputTokens}), 0)`,
49
+ } as const;
50
+
36
51
  /** One bounded page of events, newest first. */
37
52
  export interface EventPage {
38
53
  readonly events: TelemetryEventRecord[];
@@ -40,7 +55,7 @@ export interface EventPage {
40
55
  readonly nextCursor?: string;
41
56
  }
42
57
 
43
- export interface EventListQuery {
58
+ interface EventListQuery {
44
59
  readonly tenantId: string;
45
60
  /**
46
61
  * Lower bound. Omitting it means "the whole retained window" — the SQL table
@@ -96,7 +111,7 @@ export interface UsageSummary {
96
111
  readonly unpriced: number;
97
112
  }
98
113
 
99
- export interface BreakdownGroup {
114
+ interface BreakdownGroup {
100
115
  readonly key: string;
101
116
  readonly requests: number;
102
117
  readonly errors: number;
@@ -116,15 +131,8 @@ export interface ChartBucket {
116
131
  readonly errors: number;
117
132
  readonly inputTokens: number;
118
133
  readonly outputTokens: number;
119
- }
120
-
121
- export interface HealthTotals {
122
- readonly requests: number;
123
- readonly errors: number;
124
- readonly inputTokens: number;
125
- readonly outputTokens: number;
134
+ /** Cached-input share of the prompt, summed for the bucket. */
126
135
  readonly cachedInputTokens: number;
127
- readonly latencyAvgMs: number | null;
128
136
  }
129
137
 
130
138
  const DEFAULT_LIMIT = 50;
@@ -141,7 +149,7 @@ function numericNullable(value: unknown): number | null {
141
149
  }
142
150
 
143
151
  /** Maps one `telemetry_events` row to the console's record shape. */
144
- export function eventRecordFromRow(row: {
152
+ function eventRecordFromRow(row: {
145
153
  readonly id: string | null;
146
154
  readonly createdAt: Date;
147
155
  readonly tenantId: string;
@@ -176,6 +184,7 @@ export function eventRecordFromRow(row: {
176
184
  readonly firstContentDeltaAtMs: number | null;
177
185
  readonly lastEventAtMs: number | null;
178
186
  readonly payloadCaptureState: string | null;
187
+ readonly degenerateStop: boolean | null;
179
188
  }): TelemetryEventRecord {
180
189
  return {
181
190
  id: row.id ?? row.requestId,
@@ -211,6 +220,7 @@ export function eventRecordFromRow(row: {
211
220
  firstContentDeltaAtMs: row.firstContentDeltaAtMs,
212
221
  lastEventAtMs: row.lastEventAtMs,
213
222
  payloadCaptureState: row.payloadCaptureState,
223
+ ...(row.degenerateStop === true ? { degenerateStop: true } : {}),
214
224
  };
215
225
  }
216
226
 
@@ -225,11 +235,28 @@ export function eventRecordFromRow(row: {
225
235
  * renders the SQL, so it is the only answer that cannot disagree with it.
226
236
  */
227
237
  function isPostgres(db: CartethyiaDatabase): boolean {
228
- const dialect = (db as unknown as { dialect?: { constructor?: { name?: string } } }).dialect;
229
- const name = dialect?.constructor?.name;
238
+ const dialect = (db as unknown as { dialect?: unknown }).dialect;
239
+ if (dialect instanceof PgDialect) return true;
240
+ const name = (dialect as { constructor?: { name?: string } } | undefined)?.constructor?.name;
230
241
  return name === "PostgresDialect" || name === "PgDialect";
231
242
  }
232
243
 
244
+ /**
245
+ * The epoch-bucket SQL expression for one time column.
246
+ *
247
+ * Inlined (not parameterised) so Postgres sees the SELECT and GROUP BY
248
+ * expressions as identical: parameter numbers differing between the two
249
+ * clauses made Postgres treat them as unrelated expressions referencing
250
+ * `created_at`, which 42803-rejected the query. The width is a raw SQL
251
+ * literal for the same reason.
252
+ */
253
+ function bucketExpression(db: CartethyiaDatabase, column: typeof telemetryEvents.createdAt, widthSeconds: number): SQL {
254
+ const w = sql.raw(String(Math.max(1, Math.trunc(widthSeconds))));
255
+ return isPostgres(db)
256
+ ? sql<number>`(cast(extract(epoch from ${column}) as bigint) / ${w}) * ${w}`
257
+ : sql<number>`(cast(strftime('%s', ${column}) as integer) / ${w}) * ${w}`;
258
+ }
259
+
233
260
  /**
234
261
  * The tenant predicate — the scope every read shares — plus the window when
235
262
  * one is given.
@@ -400,90 +427,9 @@ export async function queryTelemetryEvents(
400
427
  };
401
428
  }
402
429
 
403
- /** One event by request id — a single indexed row, never a window scan. */
404
- export async function eventByRequestId(
405
- db: CartethyiaDatabase,
406
- tenantId: string,
407
- requestId: string,
408
- ): Promise<TelemetryEventRecord | undefined> {
409
- const [row] = await db
410
- .select()
411
- .from(telemetryEvents)
412
- .where(and(eq(telemetryEvents.tenantId, tenantId), eq(telemetryEvents.requestId, requestId)))
413
- .limit(1);
414
- if (!row) return undefined;
415
- return eventRecordFromRow(row as Parameters<typeof eventRecordFromRow>[0]);
416
- }
417
-
418
430
  /**
419
- * Totals plus latency percentiles for one tenant window.
420
- *
421
- * Percentiles are computed by the database. Postgres uses `percentile_cont`;
422
- * SQLite (no ordered-set aggregates) computes nearest-rank percentiles from
423
- * the ordered latency column via OFFSET/LIMIT. Either way, latency values are
424
- * never returned to JavaScript — the array that the old raw reader built to
425
- * sort in JS was the memory cost this replaces.
431
+ * The SQL column one breakdown dimension groups by.
426
432
  */
427
- export async function summarizeTelemetry(
428
- db: CartethyiaDatabase,
429
- tenantId: string,
430
- since: Date,
431
- ): Promise<UsageSummary> {
432
- const condition = scoped(tenantId, since);
433
- const [row] = await db
434
- .select({
435
- requests: sql<number>`count(*)`,
436
- errors: sql<number>`sum(case when ${IS_GATEWAY_ERROR_SQL} then 1 else 0 end)`,
437
- cancelled: sql<number>`sum(case when ${telemetryEvents.status} = 'cancelled' then 1 else 0 end)`,
438
- truncated: sql<number>`sum(case when ${telemetryEvents.status} = 'truncated' then 1 else 0 end)`,
439
- inputTokens: sql<number>`coalesce(sum(${telemetryEvents.inputTokens}), 0)`,
440
- outputTokens: sql<number>`coalesce(sum(${telemetryEvents.outputTokens}), 0)`,
441
- cachedInputTokens: sql<number>`coalesce(sum(${telemetryEvents.cachedInputTokens}), 0)`,
442
- reasoningTokens: sql<number>`coalesce(sum(${telemetryEvents.reasoningTokens}), 0)`,
443
- estimatedCostUsd: sql<number>`coalesce(sum(${telemetryEvents.estimatedCostUsd}), 0)`,
444
- sampled: sql<number>`count(${telemetryEvents.latencyMs})`,
445
- latencyAvg: sql<number>`avg(${telemetryEvents.latencyMs})`,
446
- unpriced: sql<number>`sum(case when ${telemetryEvents.estimatedCostUsd} is null then 1 else 0 end)`,
447
- })
448
- .from(telemetryEvents)
449
- .where(condition);
450
- if (!row) {
451
- return {
452
- requests: 0,
453
- errors: 0,
454
- cancelled: 0,
455
- truncated: 0,
456
- inputTokens: 0,
457
- outputTokens: 0,
458
- cachedInputTokens: 0,
459
- reasoningTokens: 0,
460
- estimatedCostUsd: 0,
461
- sampled: 0,
462
- latencyMs: { avg: null, p50: null, p95: null, p99: null },
463
- unpriced: 0,
464
- };
465
- }
466
-
467
- const sampled = numeric(row.sampled);
468
- const percentiles = await latencyPercentiles(db, tenantId, since, sampled);
469
- return {
470
- requests: numeric(row.requests),
471
- errors: numeric(row.errors),
472
- cancelled: numeric(row.cancelled),
473
- truncated: numeric(row.truncated),
474
- inputTokens: numeric(row.inputTokens),
475
- outputTokens: numeric(row.outputTokens),
476
- cachedInputTokens: numeric(row.cachedInputTokens),
477
- reasoningTokens: numeric(row.reasoningTokens),
478
- estimatedCostUsd: numeric(row.estimatedCostUsd),
479
- sampled,
480
- latencyMs: {
481
- avg: numericNullable(row.latencyAvg),
482
- ...percentiles,
483
- },
484
- unpriced: numeric(row.unpriced),
485
- };
486
- }
487
433
 
488
434
  type Percentiles = { p50: number | null; p95: number | null; p99: number | null };
489
435
 
@@ -546,9 +492,6 @@ async function latencyPercentiles(
546
492
  };
547
493
  }
548
494
 
549
- /**
550
- * The SQL column one breakdown dimension groups by.
551
- */
552
495
  function dimensionColumn(dimension: string) {
553
496
  if (dimension === "model") return telemetryEvents.requestedModel;
554
497
  if (dimension === "provider") return telemetryEvents.providerId;
@@ -587,9 +530,7 @@ export async function breakdownTelemetry(
587
530
  .select({
588
531
  key: column,
589
532
  requests: sql<number>`count(*)`,
590
- errors: sql<number>`sum(case when ${IS_GATEWAY_ERROR_SQL} then 1 else 0 end)`,
591
- inputTokens: sql<number>`coalesce(sum(${telemetryEvents.inputTokens}), 0)`,
592
- outputTokens: sql<number>`coalesce(sum(${telemetryEvents.outputTokens}), 0)`,
533
+ ...AGGREGATE_SELECT,
593
534
  cachedInputTokens: sql<number>`coalesce(sum(${telemetryEvents.cachedInputTokens}), 0)`,
594
535
  costUsd: sql<number>`coalesce(sum(${telemetryEvents.estimatedCostUsd}), 0)`,
595
536
  avgTokensPerSec: sql<number>`coalesce(avg(${telemetryEvents.tokensPerSec}), 0)`,
@@ -626,32 +567,23 @@ export async function chartTelemetry(
626
567
  since: Date,
627
568
  bucketSeconds: number,
628
569
  ): Promise<ChartBucket[]> {
629
- const width = Math.max(1, Math.trunc(bucketSeconds));
630
- // Inlined (not parameterised) so Postgres sees the SELECT and GROUP BY
631
- // expressions as identical: parameter numbers differing between the two
632
- // clauses made Postgres treat them as unrelated expressions referencing
633
- // `created_at`, which 42803-rejected the query.
634
- const w = sql.raw(String(width));
635
- const bucketExpression = isPostgres(db)
636
- ? sql<number>`(cast(extract(epoch from ${telemetryEvents.createdAt}) as bigint) / ${w}) * ${w}`
637
- : sql<number>`(cast(strftime('%s', ${telemetryEvents.createdAt}) as integer) / ${w}) * ${w}`;
570
+ const bucketExpr = bucketExpression(db, telemetryEvents.createdAt, bucketSeconds);
638
571
  const rows = await db
639
572
  .select({
640
- at: bucketExpression,
573
+ at: bucketExpr,
641
574
  requests: sql<number>`count(*)`,
642
- errors: sql<number>`sum(case when ${IS_GATEWAY_ERROR_SQL} then 1 else 0 end)`,
643
- inputTokens: sql<number>`coalesce(sum(${telemetryEvents.inputTokens}), 0)`,
644
- outputTokens: sql<number>`coalesce(sum(${telemetryEvents.outputTokens}), 0)`,
575
+ ...AGGREGATE_SELECT,
645
576
  })
646
577
  .from(telemetryEvents)
647
578
  .where(scoped(tenantId, since))
648
- .groupBy(bucketExpression);
579
+ .groupBy(bucketExpr);
649
580
  return rows.map((row) => ({
650
581
  at: numeric(row.at) * 1000,
651
582
  requests: numeric(row.requests),
652
583
  errors: numeric(row.errors),
653
584
  inputTokens: numeric(row.inputTokens),
654
585
  outputTokens: numeric(row.outputTokens),
586
+ cachedInputTokens: numeric(row.cachedInputTokens),
655
587
  }));
656
588
  }
657
589
 
@@ -681,20 +613,15 @@ export async function providerUsageMatrix(
681
613
  providerId: string,
682
614
  since: Date,
683
615
  ): Promise<ProviderUsageDayRow[]> {
684
- // Same inlining rule as chartTelemetry: SELECT and GROUP BY must spell the
685
- // identical expression or Postgres 42803-rejects the grouped reference.
686
- const w = sql.raw(String(86400));
687
- const dayExpression = isPostgres(db)
688
- ? sql<number>`(cast(extract(epoch from ${telemetryEvents.createdAt}) as bigint) / ${w}) * ${w}`
689
- : sql<number>`(cast(strftime('%s', ${telemetryEvents.createdAt}) as integer) / ${w}) * ${w}`;
616
+ const dayExpression = bucketExpression(db, telemetryEvents.createdAt, 86400);
690
617
  const rows = await db
691
618
  .select({
692
619
  day: dayExpression,
693
620
  model: telemetryEvents.requestedModel,
694
621
  requests: sql<number>`count(*)`,
695
- inputTokens: sql<number>`coalesce(sum(${telemetryEvents.inputTokens}), 0)`,
622
+ inputTokens: AGGREGATE_SELECT.inputTokens,
696
623
  cachedInputTokens: sql<number>`coalesce(sum(${telemetryEvents.cachedInputTokens}), 0)`,
697
- outputTokens: sql<number>`coalesce(sum(${telemetryEvents.outputTokens}), 0)`,
624
+ outputTokens: AGGREGATE_SELECT.outputTokens,
698
625
  costUsd: sql<number>`coalesce(sum(${telemetryEvents.estimatedCostUsd}), 0)`,
699
626
  })
700
627
  .from(telemetryEvents)
@@ -712,43 +639,6 @@ export async function providerUsageMatrix(
712
639
  }));
713
640
  }
714
641
 
715
- /** Overview/health totals for one tenant window. */
716
- export async function healthTelemetry(
717
- db: CartethyiaDatabase,
718
- tenantId: string,
719
- since: Date,
720
- ): Promise<HealthTotals> {
721
- const [row] = await db
722
- .select({
723
- requests: sql<number>`count(*)`,
724
- errors: sql<number>`sum(case when ${IS_GATEWAY_ERROR_SQL} then 1 else 0 end)`,
725
- inputTokens: sql<number>`coalesce(sum(${telemetryEvents.inputTokens}), 0)`,
726
- outputTokens: sql<number>`coalesce(sum(${telemetryEvents.outputTokens}), 0)`,
727
- cachedInputTokens: sql<number>`coalesce(sum(${telemetryEvents.cachedInputTokens}), 0)`,
728
- latencyAvg: sql<number>`avg(${telemetryEvents.latencyMs})`,
729
- })
730
- .from(telemetryEvents)
731
- .where(scoped(tenantId, since));
732
- if (!row) {
733
- return {
734
- requests: 0,
735
- errors: 0,
736
- inputTokens: 0,
737
- outputTokens: 0,
738
- cachedInputTokens: 0,
739
- latencyAvgMs: null,
740
- };
741
- }
742
- return {
743
- requests: numeric(row.requests),
744
- errors: numeric(row.errors),
745
- inputTokens: numeric(row.inputTokens),
746
- outputTokens: numeric(row.outputTokens),
747
- cachedInputTokens: numeric(row.cachedInputTokens),
748
- latencyAvgMs: numericNullable(row.latencyAvg),
749
- };
750
- }
751
-
752
642
  /**
753
643
  * Summed input+output tokens for a set of API keys over one window — the
754
644
  * family bucket spend query.
@@ -783,9 +673,6 @@ export async function sumTelemetryTokens(
783
673
  return numeric(row?.total ?? 0);
784
674
  }
785
675
 
786
- /** One hour, in milliseconds — the share page's hourly bucket width. */
787
- const HOUR_MS = 3_600_000;
788
-
789
676
  /**
790
677
  * All-time family counts from the durable rollup, not from a scan of the
791
678
  * retained telemetry window.
@@ -798,7 +685,7 @@ const HOUR_MS = 3_600_000;
798
685
  * event on every tick. The rollup is the same definition as the read-side
799
686
  * counts, so the two cannot drift.
800
687
  */
801
- export interface FamilyKpiTotals {
688
+ interface FamilyKpiTotals {
802
689
  readonly requests: number;
803
690
  readonly errors: number;
804
691
  readonly tokens: number;
@@ -830,43 +717,6 @@ export async function familyKpiTotals(
830
717
  };
831
718
  }
832
719
 
833
- /**
834
- * All-time family counts: the numbers a share page's KPI card shows.
835
- *
836
- * These must be computed in SQL, not derived from the bounded page read — the
837
- * page caps at {@link MAX_LIMIT}, so a busy family would freeze at 500
838
- * requests/errors/last-hour regardless of real traffic.
839
- */
840
- export interface FamilyUsageTotals {
841
- readonly requests: number;
842
- readonly errors: number;
843
- readonly lastHourRequests: number;
844
- }
845
-
846
- /** Counts one share family's requests, errors, and last-hour traffic in SQL. */
847
- export async function familyUsageTotals(
848
- db: CartethyiaDatabase,
849
- tenantId: string,
850
- familyKeyId: string,
851
- now: Date,
852
- ): Promise<FamilyUsageTotals> {
853
- const lastHourStart = new Date(now.getTime() - HOUR_MS);
854
- const conditions = [eq(telemetryEvents.tenantId, tenantId), familyKeyCondition(familyKeyId)];
855
- const [row] = await db
856
- .select({
857
- requests: sql<number>`count(*)`,
858
- errors: sql<number>`sum(case when ${IS_GATEWAY_ERROR_SQL} then 1 else 0 end)`,
859
- lastHourRequests: sql<number>`sum(case when ${telemetryEvents.createdAt} >= ${lastHourStart} then 1 else 0 end)`,
860
- })
861
- .from(telemetryEvents)
862
- .where(and(...conditions));
863
- return {
864
- requests: numeric(row?.requests ?? 0),
865
- errors: numeric(row?.errors ?? 0),
866
- lastHourRequests: numeric(row?.lastHourRequests ?? 0),
867
- };
868
- }
869
-
870
720
  /** Counts one share family's requests in a bounded window — a range scan, not a full-window aggregate. */
871
721
  export async function familyWindowRequests(
872
722
  db: CartethyiaDatabase,
@@ -945,83 +795,36 @@ export async function familyHourlyTelemetry(
945
795
  since: Date,
946
796
  bucketSeconds: number,
947
797
  ): Promise<ChartBucket[]> {
948
- const width = Math.max(1, Math.trunc(bucketSeconds));
949
- // Inlined (not parameterised) so Postgres sees the SELECT and GROUP BY
950
- // expressions as identical — see chartTelemetry for the failure this avoids.
951
- const w = sql.raw(String(width));
952
- const bucketExpression = isPostgres(db)
953
- ? sql<number>`(cast(extract(epoch from ${telemetryEvents.createdAt}) as bigint) / ${w}) * ${w}`
954
- : sql<number>`(cast(strftime('%s', ${telemetryEvents.createdAt}) as integer) / ${w}) * ${w}`;
798
+ const bucketExpr = bucketExpression(db, telemetryEvents.createdAt, bucketSeconds);
955
799
  const conditions = [scoped(tenantId, since), familyKeyCondition(familyKeyId)];
956
800
  const rows = await db
957
801
  .select({
958
- at: bucketExpression,
802
+ at: bucketExpr,
959
803
  requests: sql<number>`count(*)`,
960
- errors: sql<number>`sum(case when ${IS_GATEWAY_ERROR_SQL} then 1 else 0 end)`,
961
- inputTokens: sql<number>`coalesce(sum(${telemetryEvents.inputTokens}), 0)`,
962
- outputTokens: sql<number>`coalesce(sum(${telemetryEvents.outputTokens}), 0)`,
804
+ ...AGGREGATE_SELECT,
963
805
  })
964
806
  .from(telemetryEvents)
965
807
  .where(and(...conditions))
966
- .groupBy(bucketExpression);
808
+ .groupBy(bucketExpr);
967
809
  return rows.map((row) => ({
968
810
  at: numeric(row.at) * 1000,
969
811
  requests: numeric(row.requests),
970
812
  errors: numeric(row.errors),
971
813
  inputTokens: numeric(row.inputTokens),
972
814
  outputTokens: numeric(row.outputTokens),
815
+ cachedInputTokens: numeric(row.cachedInputTokens),
973
816
  }));
974
817
  }
975
818
 
976
- /**
977
- * The health card's latency and throughput figures for one window.
978
- *
979
- * Same principle as {@link summarizeTelemetry}: `avg(tokens_per_sec)` and the
980
- * latency percentiles are computed by the database. The console's health card
981
- * used to pull every retained event into an array to average `tokensPerSec`
982
- * and sort latencies in JavaScript.
983
- */
984
- export interface HealthPerformance {
985
- readonly latencyAvgMs: number | null;
986
- readonly latencyP50Ms: number | null;
987
- readonly latencyP95Ms: number | null;
988
- readonly latencyP99Ms: number | null;
989
- readonly avgTokensPerSec: number;
990
- }
991
-
992
- export async function summarizeHealthPerformance(
993
- db: CartethyiaDatabase,
994
- tenantId: string,
995
- since: Date,
996
- ): Promise<HealthPerformance> {
997
- const [row] = await db
998
- .select({
999
- latencyAvg: sql<number>`avg(${telemetryEvents.latencyMs})`,
1000
- sampled: sql<number>`count(${telemetryEvents.latencyMs})`,
1001
- avgTokensPerSec: sql<number>`coalesce(avg(${telemetryEvents.tokensPerSec}), 0)`,
1002
- })
1003
- .from(telemetryEvents)
1004
- .where(scoped(tenantId, since));
1005
- const sampled = numeric(row?.sampled ?? 0);
1006
- const percentiles = await latencyPercentiles(db, tenantId, since, sampled);
1007
- return {
1008
- latencyAvgMs: numericNullable(row?.latencyAvg ?? null),
1009
- latencyP50Ms: percentiles.p50,
1010
- latencyP95Ms: percentiles.p95,
1011
- latencyP99Ms: percentiles.p99,
1012
- avgTokensPerSec: numeric(row?.avgTokensPerSec ?? 0),
1013
- };
1014
- }
1015
-
1016
819
  /**
1017
820
  * The Usage summary's performance block, computed in SQL.
1018
821
  *
1019
822
  * Replaces `summarizeUsagePerformance` (which sorted a latency array in
1020
823
  * JavaScript): `avg(resolve_ms)` and `avg(tokens_per_sec)` are SQL averages,
1021
824
  * and the latency percentiles come from the same database-side percentile
1022
- * path {@link summarizeTelemetry} uses.
825
+ * path {@link summarizeUsageWithPerformance} uses.
1023
826
  */
1024
- export interface UsagePerformanceTotals {
827
+ interface UsagePerformanceTotals {
1025
828
  readonly avgResolveMs: number;
1026
829
  readonly avgDurationMs: number;
1027
830
  /**
@@ -1038,42 +841,9 @@ export interface UsagePerformanceTotals {
1038
841
  readonly avgTokensPerSec: number;
1039
842
  }
1040
843
 
1041
- export async function summarizeUsagePerformanceSql(
1042
- db: CartethyiaDatabase,
1043
- tenantId: string,
1044
- since: Date,
1045
- ): Promise<UsagePerformanceTotals> {
1046
- const [row] = await db
1047
- .select({
1048
- sampled: sql<number>`count(${telemetryEvents.latencyMs})`,
1049
- latencyAvg: sql<number>`avg(${telemetryEvents.latencyMs})`,
1050
- resolveSum: sql<number>`coalesce(sum(${telemetryEvents.resolveMs}), 0)`,
1051
- resolveCount: sql<number>`count(${telemetryEvents.resolveMs})`,
1052
- avgTokensPerSec: sql<number>`coalesce(avg(${telemetryEvents.tokensPerSec}), 0)`,
1053
- })
1054
- .from(telemetryEvents)
1055
- .where(scoped(tenantId, since));
1056
- const sampled = numeric(row?.sampled ?? 0);
1057
- const resolveCount = numeric(row?.resolveCount ?? 0);
1058
- const percentiles = await latencyPercentiles(db, tenantId, since, sampled);
1059
- return {
1060
- avgResolveMs: resolveCount > 0 ? numeric(row?.resolveSum ?? 0) / resolveCount : 0,
1061
- avgDurationMs: numericNullable(row?.latencyAvg ?? null) ?? 0,
1062
- latencyP50Ms: percentiles.p50 ?? 0,
1063
- latencyP95Ms: percentiles.p95 ?? 0,
1064
- latencyP99Ms: percentiles.p99 ?? 0,
1065
- latencySampleCount: sampled,
1066
- resolveSampleCount: resolveCount,
1067
- avgTokensPerSec: numeric(row?.avgTokensPerSec ?? 0),
1068
- };
1069
- }
1070
-
1071
844
  /**
1072
845
  * Combined usage totals + performance in ONE window scan.
1073
846
  *
1074
- * `summarizeTelemetry` and `summarizeUsagePerformanceSql` both scan the same
1075
- * tenant window and each additionally run the percentile path, so the Usage
1076
- * summary endpoint paid two scans plus two percentile passes for one response.
1077
847
  * This runs the combined SELECT once and one percentile pass, returning both
1078
848
  * shapes so the caller needs no second query.
1079
849
  */
@@ -1086,11 +856,11 @@ export async function summarizeUsageWithPerformance(
1086
856
  const [row] = await db
1087
857
  .select({
1088
858
  requests: sql<number>`count(*)`,
1089
- errors: sql<number>`sum(case when ${IS_GATEWAY_ERROR_SQL} then 1 else 0 end)`,
859
+ errors: AGGREGATE_SELECT.errors,
1090
860
  cancelled: sql<number>`sum(case when ${telemetryEvents.status} = 'cancelled' then 1 else 0 end)`,
1091
861
  truncated: sql<number>`sum(case when ${telemetryEvents.status} = 'truncated' then 1 else 0 end)`,
1092
- inputTokens: sql<number>`coalesce(sum(${telemetryEvents.inputTokens}), 0)`,
1093
- outputTokens: sql<number>`coalesce(sum(${telemetryEvents.outputTokens}), 0)`,
862
+ inputTokens: AGGREGATE_SELECT.inputTokens,
863
+ outputTokens: AGGREGATE_SELECT.outputTokens,
1094
864
  cachedInputTokens: sql<number>`coalesce(sum(${telemetryEvents.cachedInputTokens}), 0)`,
1095
865
  reasoningTokens: sql<number>`coalesce(sum(${telemetryEvents.reasoningTokens}), 0)`,
1096
866
  estimatedCostUsd: sql<number>`coalesce(sum(${telemetryEvents.estimatedCostUsd}), 0)`,
@@ -36,7 +36,7 @@ export const LOG_RING_MAX_CODE_UNITS = 8_192;
36
36
 
37
37
  /** Marker appended wherever content was dropped. */
38
38
  export const LOG_TRUNCATED_MARKER = "[truncated]";
39
- export const LOG_CIRCULAR_MARKER = "[circular]";
39
+ const LOG_CIRCULAR_MARKER = "[circular]";
40
40
 
41
41
  function isHighSurrogate(code: number): boolean {
42
42
  return code >= 0xd800 && code <= 0xdbff;
@@ -5,18 +5,18 @@
5
5
  * `render()` emits the Prometheus exposition format on demand.
6
6
  */
7
7
 
8
- export type MetricLabels = Readonly<Record<string, string | number>>;
8
+ type MetricLabels = Readonly<Record<string, string | number>>;
9
9
 
10
- export interface CounterMetric {
10
+ interface CounterMetric {
11
11
  inc(value?: number, labels?: MetricLabels): void;
12
12
  }
13
13
 
14
- export interface GaugeMetric extends CounterMetric {
14
+ interface GaugeMetric extends CounterMetric {
15
15
  set(value: number, labels?: MetricLabels): void;
16
16
  dec(value?: number, labels?: MetricLabels): void;
17
17
  }
18
18
 
19
- export interface HistogramMetric {
19
+ interface HistogramMetric {
20
20
  observe(value: number, labels?: MetricLabels): void;
21
21
  }
22
22
 
@@ -8,7 +8,7 @@ import type {
8
8
  TelemetryPayloadDepth,
9
9
  TelemetryPayloadMode,
10
10
  } from "../console/settings/contracts";
11
- export type CaptureScope = "tenant" | "debug_session" | "operator_flag";
11
+ type CaptureScope = "tenant" | "debug_session" | "operator_flag";
12
12
 
13
13
  /**
14
14
  * How long a captured payload frame lives when the deployment has not chosen.
@@ -203,13 +203,13 @@ export interface PayloadCaptureInput {
203
203
  operatorFlagOptIn?: boolean;
204
204
  }
205
205
 
206
- export interface PayloadSignals {
206
+ interface PayloadSignals {
207
207
  readonly toolCalls: number;
208
208
  readonly images: number;
209
209
  readonly attachments: number;
210
210
  }
211
211
 
212
- export interface StoredPayload {
212
+ interface StoredPayload {
213
213
  id: string;
214
214
  /**
215
215
  * JSON text of the five body surfaces, in drawer order, built while the
@@ -412,8 +412,9 @@ export function buildPayloadRecord(
412
412
 
413
413
  /**
414
414
  * Postgres-backed redacted request/response payload capture. Payloads live for
415
- * the tenant's chosen retention — 15 minutes by default — and are pruned by the
416
- * sweep.
415
+ * the tenant's chosen retention — an hour by default (see
416
+ * {@link payloadRetentionMs} / `DEFAULT_PAYLOAD_RETENTION_MS`) — and are pruned
417
+ * by the sweep.
417
418
 
418
419
  */
419
420
  export class TelemetryPayloadCapture {
@@ -22,7 +22,7 @@ interface PayloadFrame {
22
22
  readonly payload: unknown;
23
23
  }
24
24
 
25
- export function isPayloadFileReference(value: unknown): value is PayloadFileReference {
25
+ function isPayloadFileReference(value: unknown): value is PayloadFileReference {
26
26
  if (typeof value !== "object" || value === null) return false;
27
27
  const record = value as Record<string, unknown>;
28
28
  return (
@@ -32,7 +32,7 @@ export interface RuntimeMemorySnapshot {
32
32
  }
33
33
 
34
34
  /** Live collection sizes sampled alongside process memory. */
35
- export interface RuntimeMetricSources {
35
+ interface RuntimeMetricSources {
36
36
  readonly routingRoundRobinEntries: () => { readonly combo: number; readonly provider: number };
37
37
  /**
38
38
  * Tracked keys in the in-memory IP abuse store, or 0 when the store is
@@ -65,6 +65,12 @@ export interface TelemetryEventInput {
65
65
  * list instead of needing the server's logs.
66
66
  */
67
67
  readonly payloadCaptureState?: string;
68
+ /**
69
+ * True for a completed request whose output was far smaller than the model's
70
+ * ceiling and which ended with an ordinary `stop` (a "degenerate stop").
71
+ * Carried onto the telemetry row so this pattern can be measured.
72
+ */
73
+ readonly degenerateStop?: boolean;
68
74
  }
69
75
 
70
76
  function tokenCountToInt(value: number | "unavailable" | undefined): number | undefined {
@@ -155,10 +161,11 @@ function telemetryEventRow(event: TelemetryEventInput) {
155
161
  firstContentDeltaAtMs: event.firstContentDeltaAtMs ?? null,
156
162
  lastEventAtMs: event.lastEventAtMs ?? null,
157
163
  payloadCaptureState: event.payloadCaptureState ?? null,
164
+ degenerateStop: event.degenerateStop ?? null,
158
165
  };
159
166
  }
160
167
 
161
- export interface TelemetryBatchBufferOptions {
168
+ interface TelemetryBatchBufferOptions {
162
169
  readonly flushIntervalMs?: number;
163
170
  readonly maxBatch?: number;
164
171
  readonly maxItems?: number;
@@ -184,8 +191,17 @@ function drainRetryDelayMs(attempt: number, baseDelayMs = DRAIN_RETRY_BASE_MS):
184
191
  return exponentialBackoff(attempt, options);
185
192
  }
186
193
 
187
- /** Floor for the adaptive active interval so a full queue still drains promptly. */
188
- const ADAPTIVE_FLUSH_MIN_MS = 50;
194
+ /**
195
+ * Floor for the adaptive active interval so a full queue still drains promptly.
196
+ *
197
+ * Raised to 200ms (from 50ms) so a flush does not collide with console reads:
198
+ * under load the queue saturates `maxBatch`, the interval fell to 50ms, and a
199
+ * drain landed in the middle of the console's own telemetry reads — the GROUP BY
200
+ * behind the Usage/ConsoleLog pages — making the page's queries wait on the
201
+ * drain's insert lock. 200ms still drains a 500-row batch ~5x/sec, which is a
202
+ * steady 2500 rows/sec ceiling; the adaptive curve otherwise keeps its shape.
203
+ */
204
+ const ADAPTIVE_FLUSH_MIN_MS = 200;
189
205
  /** Multiplier applied to the base interval while the queue is empty. */
190
206
  const ADAPTIVE_IDLE_MULTIPLIER = 4;
191
207