cartethyia 2.5.10 → 2.6.0

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 (367) hide show
  1. package/.env.example +8 -0
  2. package/dist/dashboard/assets/{BannedPage-Cd30Z0b9.js → BannedPage-l6XMnfr9.js} +1 -1
  3. package/dist/dashboard/assets/BannedPage-l6XMnfr9.js.gz +0 -0
  4. package/dist/dashboard/assets/{CliToolDetailPage-wf5u-ww6.js → CliToolDetailPage-DwOzAW45.js} +1 -1
  5. package/dist/dashboard/assets/CliToolDetailPage-DwOzAW45.js.gz +0 -0
  6. package/dist/dashboard/assets/{CliToolsPage-DykPvuck.js → CliToolsPage-BT3Up3QA.js} +1 -1
  7. package/dist/dashboard/assets/CliToolsPage-BT3Up3QA.js.gz +0 -0
  8. package/dist/dashboard/assets/{CombosPage-DwvSD3vs.js → CombosPage-6kYIUpzR.js} +1 -1
  9. package/dist/dashboard/assets/CombosPage-6kYIUpzR.js.gz +0 -0
  10. package/dist/dashboard/assets/{ConsoleLogPage-CIr7KDex.js → ConsoleLogPage-zRnj1mlR.js} +1 -1
  11. package/dist/dashboard/assets/ConsoleLogPage-zRnj1mlR.js.gz +0 -0
  12. package/dist/dashboard/assets/{CustomizationPage-82X0DDdH.js → CustomizationPage-C7m-H1Rz.js} +1 -1
  13. package/dist/dashboard/assets/CustomizationPage-C7m-H1Rz.js.gz +0 -0
  14. package/dist/dashboard/assets/HealthEventsModal-C8MR_BdB.js +1 -0
  15. package/dist/dashboard/assets/HealthEventsModal-C8MR_BdB.js.gz +0 -0
  16. package/dist/dashboard/assets/{LoginPage-CS5mPuVF.js → LoginPage-CbphLvjg.js} +1 -1
  17. package/dist/dashboard/assets/LoginPage-CbphLvjg.js.gz +0 -0
  18. package/dist/dashboard/assets/{ModelPicker-C0UCUsVJ.js → ModelPicker-DVny4S5_.js} +1 -1
  19. package/dist/dashboard/assets/ModelPicker-DVny4S5_.js.gz +0 -0
  20. package/dist/dashboard/assets/OverviewPage-DABu2Wgt.js +1 -0
  21. package/dist/dashboard/assets/OverviewPage-DABu2Wgt.js.gz +0 -0
  22. package/dist/dashboard/assets/ProviderDetailPage-C15x9q13.js +3 -0
  23. package/dist/dashboard/assets/ProviderDetailPage-C15x9q13.js.gz +0 -0
  24. package/dist/dashboard/assets/{ProvidersPage-vdja5ZPK.js → ProvidersPage-Dfb2896L.js} +1 -1
  25. package/dist/dashboard/assets/ProvidersPage-Dfb2896L.js.gz +0 -0
  26. package/dist/dashboard/assets/{ProxyPage-BB4CNm4-.js → ProxyPage-BaCIdAUt.js} +1 -1
  27. package/dist/dashboard/assets/ProxyPage-BaCIdAUt.js.gz +0 -0
  28. package/dist/dashboard/assets/QuotaPage-BsdoCIw1.js +1 -0
  29. package/dist/dashboard/assets/QuotaPage-BsdoCIw1.js.gz +0 -0
  30. package/dist/dashboard/assets/SettingsPage-KVJFh3Ip.js +1 -0
  31. package/dist/dashboard/assets/SettingsPage-KVJFh3Ip.js.gz +0 -0
  32. package/dist/dashboard/assets/{SetupPage-FadUOl9S.js → SetupPage-D4g6vOGc.js} +1 -1
  33. package/dist/dashboard/assets/SetupPage-D4g6vOGc.js.gz +0 -0
  34. package/dist/dashboard/assets/{SortableList-Cfy6_Khv.js → SortableList-C93i14BD.js} +1 -1
  35. package/dist/dashboard/assets/SortableList-C93i14BD.js.gz +0 -0
  36. package/dist/dashboard/assets/{StudioPage-CM92fgJf.js → StudioPage-SgQpymjm.js} +1 -1
  37. package/dist/dashboard/assets/{StudioPage-CM92fgJf.js.gz → StudioPage-SgQpymjm.js.gz} +0 -0
  38. package/dist/dashboard/assets/{UsagePage-BT4MyQeT.js → UsagePage-BFME-eba.js} +2 -2
  39. package/dist/dashboard/assets/UsagePage-BFME-eba.js.gz +0 -0
  40. package/dist/dashboard/assets/{api-keys-DQYLQxpW.js → api-keys-C23S9sgp.js} +1 -1
  41. package/dist/dashboard/assets/api-keys-C23S9sgp.js.gz +0 -0
  42. package/dist/dashboard/assets/{app-B7pVRhJ1.js → app-2KhtHCv9.js} +1 -1
  43. package/dist/dashboard/assets/{app-B7pVRhJ1.js.gz → app-2KhtHCv9.js.gz} +0 -0
  44. package/dist/dashboard/assets/app-CEeaMHOy.css +2 -0
  45. package/dist/dashboard/assets/app-CEeaMHOy.css.gz +0 -0
  46. package/dist/dashboard/assets/{assert-C9FD_JwJ.js → assert-DU6qrMA_.js} +1 -1
  47. package/dist/dashboard/assets/assert-DU6qrMA_.js.gz +0 -0
  48. package/dist/dashboard/assets/{badge-Bvyr7D0u.js → badge-CKsF123d.js} +1 -1
  49. package/dist/dashboard/assets/badge-CKsF123d.js.gz +0 -0
  50. package/dist/dashboard/assets/{banner-DIvKAZih.js → banner-8j7uChA9.js} +1 -1
  51. package/dist/dashboard/assets/banner-8j7uChA9.js.gz +0 -0
  52. package/dist/dashboard/assets/{card-D5_gYIS_.js → card-CdsSlWA3.js} +1 -1
  53. package/dist/dashboard/assets/card-CdsSlWA3.js.gz +0 -0
  54. package/dist/dashboard/assets/{cli-tools-CNqzrfdb.js → cli-tools-BaFUuFFF.js} +1 -1
  55. package/dist/dashboard/assets/cli-tools-BaFUuFFF.js.gz +0 -0
  56. package/dist/dashboard/assets/{clipboard-button-B6vDBtbm.js → clipboard-button-uL5aYLfW.js} +1 -1
  57. package/dist/dashboard/assets/clipboard-button-uL5aYLfW.js.gz +0 -0
  58. package/dist/dashboard/assets/{dialog-CpjrgdwJ.js → dialog-C07hr5_G.js} +1 -1
  59. package/dist/dashboard/assets/dialog-C07hr5_G.js.gz +0 -0
  60. package/dist/dashboard/assets/{drawer-BWmt3b5y.js → drawer-BPO1TcKH.js} +1 -1
  61. package/dist/dashboard/assets/drawer-BPO1TcKH.js.gz +0 -0
  62. package/dist/dashboard/assets/{entry-C_oakV3z.js → entry-Bz_CtDfC.js} +1 -1
  63. package/dist/dashboard/assets/entry-Bz_CtDfC.js.gz +0 -0
  64. package/dist/dashboard/assets/entry-CwPRKk0d.js +4 -0
  65. package/dist/dashboard/assets/entry-CwPRKk0d.js.gz +0 -0
  66. package/dist/dashboard/assets/entry-GJRbmrpA.js +1 -0
  67. package/dist/dashboard/assets/entry-GJRbmrpA.js.gz +0 -0
  68. package/dist/dashboard/assets/{github-badge-RRHHWGxh.js → github-badge-DmQreSDd.js} +1 -1
  69. package/dist/dashboard/assets/github-badge-DmQreSDd.js.gz +0 -0
  70. package/dist/dashboard/assets/{index-Bhey_rg_.js → index-Bblu6erR.js} +2 -2
  71. package/dist/dashboard/assets/index-Bblu6erR.js.gz +0 -0
  72. package/dist/dashboard/assets/{inline-C2_EQ5Uf.js → inline-C9dCYaBZ.js} +1 -1
  73. package/dist/dashboard/assets/inline-C9dCYaBZ.js.gz +0 -0
  74. package/dist/dashboard/assets/{input-BMRlQ450.js → input-CjCnXweJ.js} +1 -1
  75. package/dist/dashboard/assets/input-CjCnXweJ.js.gz +0 -0
  76. package/dist/dashboard/assets/{layout-XR5xP8ew.js → layout-V6y8DAkl.js} +1 -1
  77. package/dist/dashboard/assets/layout-V6y8DAkl.js.gz +0 -0
  78. package/dist/dashboard/assets/{list-row-Cw07dioc.js → list-row-CM1AaG_y.js} +1 -1
  79. package/dist/dashboard/assets/list-row-CM1AaG_y.js.gz +0 -0
  80. package/dist/dashboard/assets/{live-BivGfuUs.js → live-ta6GMb-8.js} +1 -1
  81. package/dist/dashboard/assets/live-ta6GMb-8.js.gz +0 -0
  82. package/dist/dashboard/assets/{model-bans-CmffjLuG.js → model-bans-BmYTmbAk.js} +1 -1
  83. package/dist/dashboard/assets/model-bans-BmYTmbAk.js.gz +0 -0
  84. package/dist/dashboard/assets/{mutation-46P0oWMe.js → mutation-g_fduiNw.js} +1 -1
  85. package/dist/dashboard/assets/mutation-g_fduiNw.js.gz +0 -0
  86. package/dist/dashboard/assets/{numbers-DrdA9oSM.js → numbers-BOevJzdz.js} +1 -1
  87. package/dist/dashboard/assets/numbers-BOevJzdz.js.gz +0 -0
  88. package/dist/dashboard/assets/{query-keys-BFhGpwR1.js → query-keys-UETqfytp.js} +1 -1
  89. package/dist/dashboard/assets/query-keys-UETqfytp.js.gz +0 -0
  90. package/dist/dashboard/assets/{routing-CAb4F37R.js → routing-BPUo-W8d.js} +1 -1
  91. package/dist/dashboard/assets/routing-BPUo-W8d.js.gz +0 -0
  92. package/dist/dashboard/assets/{select-CxVVB__N.js → select-C980c4cz.js} +1 -1
  93. package/dist/dashboard/assets/select-C980c4cz.js.gz +0 -0
  94. package/dist/dashboard/assets/{settings-DNX9sydi.js → settings--3hxSyry.js} +1 -1
  95. package/dist/dashboard/assets/settings--3hxSyry.js.gz +0 -0
  96. package/dist/dashboard/assets/{stack-uPOwM9Hs.js → stack-_gfTdLVB.js} +1 -1
  97. package/dist/dashboard/assets/stack-_gfTdLVB.js.gz +0 -0
  98. package/dist/dashboard/assets/{state-D1Yz79iL.js → state-2c5V4zo7.js} +1 -1
  99. package/dist/dashboard/assets/state-2c5V4zo7.js.gz +0 -0
  100. package/dist/dashboard/assets/{switch-sevsRrRo.js → switch-Dkke7Naf.js} +1 -1
  101. package/dist/dashboard/assets/switch-Dkke7Naf.js.gz +0 -0
  102. package/dist/dashboard/assets/{useMutation-Dem0lXQj.js → useMutation-EDFRkn1i.js} +1 -1
  103. package/dist/dashboard/assets/useMutation-EDFRkn1i.js.gz +0 -0
  104. package/dist/dashboard/index.html +1 -1
  105. package/dist/dashboard/index.html.gz +0 -0
  106. package/dist/dashboard/precompressed-assets.json +51 -51
  107. package/migrations/README.md +52 -52
  108. package/migrations/full-postgres/0020_add_telemetry_event_analytics.sql +77 -0
  109. package/migrations/full-postgres/0021_add_api_key_concurrency_tolerance.sql +13 -0
  110. package/migrations/full-postgres/0022_add_inferhub_config_key.sql +17 -0
  111. package/migrations/full-postgres/0023_drop_api_key_concurrency_tolerance.sql +13 -0
  112. package/migrations/lite-sqlite3/0020_add_telemetry_event_analytics.sql +69 -0
  113. package/migrations/lite-sqlite3/0021_add_api_key_concurrency_tolerance.sql +13 -0
  114. package/migrations/lite-sqlite3/0022_add_inferhub_config_key.sql +17 -0
  115. package/migrations/lite-sqlite3/0023_drop_api_key_concurrency_tolerance.sql +13 -0
  116. package/package.json +96 -95
  117. package/scripts/cli/admin.ts +126 -128
  118. package/scripts/cli/autostart.ts +237 -237
  119. package/scripts/cli/background.ts +120 -110
  120. package/scripts/cli/bin/carte.cjs +8 -8
  121. package/scripts/cli/bin/cartethyia.cjs +167 -167
  122. package/scripts/cli/console-menu.ts +39 -30
  123. package/scripts/cli/controller.ts +145 -0
  124. package/scripts/cli/env.ts +99 -99
  125. package/scripts/cli/gateway.ts +264 -260
  126. package/scripts/cli/instance.ts +234 -224
  127. package/scripts/cli/main.ts +188 -167
  128. package/scripts/cli/platform.ts +64 -64
  129. package/scripts/cli/prompt.ts +217 -79
  130. package/scripts/cli/services.ts +88 -88
  131. package/scripts/cli/setup.ts +445 -445
  132. package/scripts/cli/start.ts +85 -85
  133. package/scripts/cli/startup-signal.ts +23 -23
  134. package/scripts/cli/telemetry.ts +70 -0
  135. package/scripts/cli/tray/index.ts +263 -263
  136. package/scripts/cli/tray/windows.ps1 +107 -107
  137. package/scripts/cli/tray.ts +29 -4
  138. package/scripts/cli/update.ts +39 -39
  139. package/scripts/cli/version.ts +67 -67
  140. package/scripts/cli/wordmark.ts +58 -58
  141. package/src/boot/app.ts +16 -1
  142. package/src/boot/dependencies.ts +128 -25
  143. package/src/config/resources.ts +137 -0
  144. package/src/config/runtime.ts +133 -76
  145. package/src/config/spec.ts +30 -29
  146. package/src/config/telemetry.ts +0 -25
  147. package/src/console/api-keys/bucket-spend.ts +14 -17
  148. package/src/console/api-keys/contracts.ts +410 -410
  149. package/src/console/api-keys/routes.ts +12 -0
  150. package/src/console/audit/store.ts +2 -3
  151. package/src/console/auth/setup-service.ts +8 -1
  152. package/src/console/backup/routes.ts +7 -0
  153. package/src/console/backup/service.ts +463 -463
  154. package/src/console/backup/store.ts +16 -11
  155. package/src/console/backup/validate.ts +1 -4
  156. package/src/console/cli-tools/fs-ops.ts +18 -2
  157. package/src/console/data-management/contracts.ts +1 -8
  158. package/src/console/data-management/operations.ts +61 -48
  159. package/src/console/data-management/routes.ts +4 -2
  160. package/src/console/domain-registration.ts +25 -3
  161. package/src/console/inferhub/client.ts +139 -0
  162. package/src/console/inferhub/key-store.ts +129 -0
  163. package/src/console/inferhub/normalize.ts +347 -0
  164. package/src/console/inferhub/routes.ts +411 -0
  165. package/src/console/inferhub/store.ts +109 -0
  166. package/src/console/inferhub/types.ts +183 -0
  167. package/src/console/observability/contracts.ts +2 -41
  168. package/src/console/observability/store.ts +176 -371
  169. package/src/console/providers/catalog/public-model-store.ts +0 -0
  170. package/src/console/providers/catalog/store.ts +23 -36
  171. package/src/console/quota/account-quota-shared.ts +52 -52
  172. package/src/console/quota/account-quota-tenant.ts +16 -24
  173. package/src/console/quota/refresh.ts +441 -441
  174. package/src/console/routing/pools/import-parse.ts +206 -206
  175. package/src/console/routing/pools/operations.ts +543 -543
  176. package/src/console/routing/pools/probe-verdict.ts +132 -132
  177. package/src/console/routing/pools/routes.ts +181 -181
  178. package/src/console/settings/contracts.ts +42 -40
  179. package/src/console/settings/store.ts +25 -25
  180. package/src/console/share/share-stats.ts +22 -4
  181. package/src/console/share/share-usage.ts +30 -4
  182. package/src/console/shared/cursor.ts +15 -0
  183. package/src/console/updates/routes.ts +12 -0
  184. package/src/main.ts +13 -5
  185. package/src/network/pool/resolver.ts +1104 -1104
  186. package/src/observability/client-name.ts +22 -0
  187. package/src/observability/event-log.ts +5 -29
  188. package/src/observability/event-query.ts +690 -0
  189. package/src/observability/log-ring.ts +26 -2
  190. package/src/observability/log-sanitize.ts +118 -0
  191. package/src/observability/logger.ts +17 -13
  192. package/src/observability/metrics.ts +35 -0
  193. package/src/observability/payload-capture.ts +114 -41
  194. package/src/observability/payload-store.ts +249 -24
  195. package/src/observability/raw-event-writer.ts +118 -0
  196. package/src/observability/runtime-metrics.ts +71 -1
  197. package/src/observability/telemetry-buffer.ts +100 -17
  198. package/src/observability/telemetry-reconciliation.ts +417 -0
  199. package/src/persistence/boot.ts +20 -9
  200. package/src/persistence/db-pg.ts +13 -0
  201. package/src/persistence/db-sqlite.ts +569 -497
  202. package/src/persistence/dialect.ts +3 -2
  203. package/src/persistence/migrate.ts +181 -181
  204. package/src/persistence/schema.ts +1496 -1406
  205. package/src/persistence/shared-cache.ts +5 -1
  206. package/src/persistence/stores/api-key-store.ts +581 -581
  207. package/src/persistence/stores/telemetry-store.ts +124 -63
  208. package/src/protocol/reasoning-text.ts +30 -0
  209. package/src/protocol/response/chat.ts +33 -7
  210. package/src/protocol/response/codex.ts +14 -2
  211. package/src/protocol/response/messages.ts +5 -2
  212. package/src/providers/authentication/oauth-flow-store.ts +3 -10
  213. package/src/providers/bundled-discovery-providers.ts +122 -122
  214. package/src/providers/default-registry.ts +10 -0
  215. package/src/providers/discovery/catalog/catalog-blob.ts +18 -18
  216. package/src/providers/discovery/models-dev-catalog.ts +354 -354
  217. package/src/providers/discovery/probe-phases.ts +43 -7
  218. package/src/providers/discovery/probing-service.ts +70 -32
  219. package/src/providers/integrations/antigravity/antigravity.ts +9 -2
  220. package/src/providers/integrations/buddy/buddy-catalog-shared.ts +85 -17
  221. package/src/providers/integrations/buddy/buddy-chat-shared.ts +934 -934
  222. package/src/providers/integrations/buddy/buddy-checkin.ts +5 -9
  223. package/src/providers/integrations/buddy/buddy-discovery-shared.ts +3 -7
  224. package/src/providers/integrations/buddy/buddy-quota-shared.ts +3 -1
  225. package/src/providers/integrations/buddy/codebuddy/cn-provider.ts +57 -49
  226. package/src/providers/integrations/buddy/codebuddy/provider.ts +180 -180
  227. package/src/providers/integrations/buddy/workbuddy/provider.ts +206 -206
  228. package/src/providers/integrations/buddy/workbuddy/quota.ts +161 -161
  229. package/src/providers/integrations/claude/claude-quota.ts +2 -2
  230. package/src/providers/integrations/claude/claude-user-profiles.ts +3 -2
  231. package/src/providers/integrations/claude/claude.ts +3 -4
  232. package/src/providers/integrations/cline/cline.ts +94 -8
  233. package/src/providers/integrations/codex/codex.ts +61 -37
  234. package/src/providers/integrations/cursor/cursor-catalog.ts +368 -368
  235. package/src/providers/integrations/cursor/cursor-http2.ts +300 -300
  236. package/src/providers/integrations/cursor/cursor-identity.ts +2 -4
  237. package/src/providers/integrations/cursor/cursor-request.ts +11 -0
  238. package/src/providers/integrations/cursor/cursor.ts +29 -17
  239. package/src/providers/integrations/cursor/protocol/framing.ts +129 -129
  240. package/src/providers/integrations/cursor/protocol/messages.ts +2380 -2380
  241. package/src/providers/integrations/cursor/protocol/struct.ts +305 -305
  242. package/src/providers/integrations/cursor/protocol/tool-canonical.ts +275 -0
  243. package/src/providers/integrations/grok/grok-quota.ts +279 -279
  244. package/src/providers/integrations/grok/grok-ratelimit-store.ts +73 -73
  245. package/src/providers/integrations/grok/grok-ratelimit.ts +218 -218
  246. package/src/providers/integrations/hermes.ts +7 -1
  247. package/src/providers/integrations/inferhub.ts +201 -73
  248. package/src/providers/integrations/kilo/kilo.ts +152 -152
  249. package/src/providers/integrations/kiro/kiro-machine-id.ts +2 -4
  250. package/src/providers/integrations/openrouter/provider.ts +129 -129
  251. package/src/providers/integrations/qoder/qoder-quota.ts +193 -192
  252. package/src/providers/integrations/qoder/qoder-realm.ts +106 -106
  253. package/src/providers/integrations/trae/trae-oauth.ts +4 -26
  254. package/src/providers/integrations/trae/trae-quota.ts +389 -384
  255. package/src/providers/integrations/trae/trae-realm.ts +15 -2
  256. package/src/providers/integrations/xiaomi-mimo/think-stream.ts +100 -24
  257. package/src/providers/integrations/zcode/oauth.ts +1 -1
  258. package/src/providers/operations/account-health-service.ts +605 -279
  259. package/src/providers/provider-metadata.ts +12 -1
  260. package/src/providers/provider-model-definition.ts +373 -373
  261. package/src/providers/quota/quota-contracts.ts +3 -7
  262. package/src/providers/search/search-provider.ts +2 -6
  263. package/src/security/admission/contracts.ts +10 -0
  264. package/src/security/admission/in-memory-store.ts +89 -14
  265. package/src/security/admission/lua.ts +191 -182
  266. package/src/security/admission/redis-store.ts +30 -0
  267. package/src/security/admission/service.ts +18 -0
  268. package/src/security/api-key-auth-cache.ts +96 -0
  269. package/src/security/api-key-auth.ts +11 -100
  270. package/src/security/client-router-fingerprint.ts +3 -7
  271. package/src/security/ip-boundary.ts +7 -3
  272. package/src/shared/coerce.ts +67 -0
  273. package/src/shared/hash.ts +6 -0
  274. package/src/shared/time.ts +7 -0
  275. package/src/transport/dispatch/attempt-finalize.ts +26 -138
  276. package/src/transport/dispatch/attempt-loop.ts +103 -81
  277. package/src/transport/dispatch/capture-policy.ts +78 -0
  278. package/src/transport/dispatch/proxy-request.ts +4 -2
  279. package/src/transport/dispatch/streaming/encoder.ts +111 -5
  280. package/src/transport/dispatch/streaming/stream.ts +19 -2
  281. package/src/transport/dispatch/streaming/thinking-loop.ts +300 -0
  282. package/src/transport/failure-policy.ts +27 -0
  283. package/src/transport/guards/abuse-guard.ts +2 -2
  284. package/src/transport/guards/auth-guard.ts +17 -0
  285. package/src/transport/guards/body-policy.ts +3 -2
  286. package/src/transport/guards/concurrency-guard.ts +89 -13
  287. package/src/transport/guards/early-rejection-capture.ts +43 -3
  288. package/src/transport/guards/error-lifecycle.ts +10 -2
  289. package/src/transport/guards/guard-errors.ts +23 -0
  290. package/src/transport/guards/headroom-guard.ts +41 -7
  291. package/src/transport/guards/pipeline.ts +24 -0
  292. package/src/transport/guards/request-context.ts +2 -1
  293. package/src/transport/routing/admission.ts +51 -9
  294. package/src/transport/routing/bounded-lru.ts +49 -0
  295. package/src/transport/routing/candidate-index.ts +88 -0
  296. package/src/transport/routing/reservations.ts +121 -7
  297. package/src/transport/routing/round-robin.ts +10 -22
  298. package/src/transport/routing/route-catalog.ts +26 -13
  299. package/src/transport/routing/route-model.ts +22 -2
  300. package/src/transport/routing/router.ts +53 -31
  301. package/src/transport/surface/chat/encode.ts +2 -3
  302. package/src/transport/surface/chat/parse.ts +2 -1
  303. package/src/transport/surface/dialects.ts +11 -3
  304. package/src/transport/surface/messages/encode.ts +2 -1
  305. package/src/transport/surface/messages/parse.ts +3 -1
  306. package/src/transport/surface/messages/stream.ts +2 -6
  307. package/src/workers/maintenance-lease.ts +200 -0
  308. package/src/workers/scheduler.ts +101 -1
  309. package/dist/dashboard/assets/BannedPage-Cd30Z0b9.js.gz +0 -0
  310. package/dist/dashboard/assets/CliToolDetailPage-wf5u-ww6.js.gz +0 -0
  311. package/dist/dashboard/assets/CliToolsPage-DykPvuck.js.gz +0 -0
  312. package/dist/dashboard/assets/CombosPage-DwvSD3vs.js.gz +0 -0
  313. package/dist/dashboard/assets/ConsoleLogPage-CIr7KDex.js.gz +0 -0
  314. package/dist/dashboard/assets/CustomizationPage-82X0DDdH.js.gz +0 -0
  315. package/dist/dashboard/assets/HealthEventsModal-BYPucTTn.js +0 -1
  316. package/dist/dashboard/assets/HealthEventsModal-BYPucTTn.js.gz +0 -0
  317. package/dist/dashboard/assets/LoginPage-CS5mPuVF.js.gz +0 -0
  318. package/dist/dashboard/assets/ModelPicker-C0UCUsVJ.js.gz +0 -0
  319. package/dist/dashboard/assets/OverviewPage-Dk_ew3YU.js +0 -1
  320. package/dist/dashboard/assets/OverviewPage-Dk_ew3YU.js.gz +0 -0
  321. package/dist/dashboard/assets/ProviderDetailPage-CAiRn2Q0.js +0 -3
  322. package/dist/dashboard/assets/ProviderDetailPage-CAiRn2Q0.js.gz +0 -0
  323. package/dist/dashboard/assets/ProvidersPage-vdja5ZPK.js.gz +0 -0
  324. package/dist/dashboard/assets/ProxyPage-BB4CNm4-.js.gz +0 -0
  325. package/dist/dashboard/assets/QuotaPage-FEc4iNyq.js +0 -1
  326. package/dist/dashboard/assets/QuotaPage-FEc4iNyq.js.gz +0 -0
  327. package/dist/dashboard/assets/SettingsPage-Dlfi5L4N.js +0 -1
  328. package/dist/dashboard/assets/SettingsPage-Dlfi5L4N.js.gz +0 -0
  329. package/dist/dashboard/assets/SetupPage-FadUOl9S.js.gz +0 -0
  330. package/dist/dashboard/assets/SortableList-Cfy6_Khv.js.gz +0 -0
  331. package/dist/dashboard/assets/UsagePage-BT4MyQeT.js.gz +0 -0
  332. package/dist/dashboard/assets/api-keys-DQYLQxpW.js.gz +0 -0
  333. package/dist/dashboard/assets/app-CX_Z8cMs.css +0 -2
  334. package/dist/dashboard/assets/app-CX_Z8cMs.css.gz +0 -0
  335. package/dist/dashboard/assets/assert-C9FD_JwJ.js.gz +0 -0
  336. package/dist/dashboard/assets/badge-Bvyr7D0u.js.gz +0 -0
  337. package/dist/dashboard/assets/banner-DIvKAZih.js.gz +0 -0
  338. package/dist/dashboard/assets/card-D5_gYIS_.js.gz +0 -0
  339. package/dist/dashboard/assets/cli-tools-CNqzrfdb.js.gz +0 -0
  340. package/dist/dashboard/assets/clipboard-button-B6vDBtbm.js.gz +0 -0
  341. package/dist/dashboard/assets/dialog-CpjrgdwJ.js.gz +0 -0
  342. package/dist/dashboard/assets/drawer-BWmt3b5y.js.gz +0 -0
  343. package/dist/dashboard/assets/entry-B-gvimrg.js +0 -1
  344. package/dist/dashboard/assets/entry-B-gvimrg.js.gz +0 -0
  345. package/dist/dashboard/assets/entry-C_oakV3z.js.gz +0 -0
  346. package/dist/dashboard/assets/entry-Dj0cHfEc.js +0 -4
  347. package/dist/dashboard/assets/entry-Dj0cHfEc.js.gz +0 -0
  348. package/dist/dashboard/assets/github-badge-RRHHWGxh.js.gz +0 -0
  349. package/dist/dashboard/assets/index-Bhey_rg_.js.gz +0 -0
  350. package/dist/dashboard/assets/inline-C2_EQ5Uf.js.gz +0 -0
  351. package/dist/dashboard/assets/input-BMRlQ450.js.gz +0 -0
  352. package/dist/dashboard/assets/layout-XR5xP8ew.js.gz +0 -0
  353. package/dist/dashboard/assets/list-row-Cw07dioc.js.gz +0 -0
  354. package/dist/dashboard/assets/live-BivGfuUs.js.gz +0 -0
  355. package/dist/dashboard/assets/model-bans-CmffjLuG.js.gz +0 -0
  356. package/dist/dashboard/assets/mutation-46P0oWMe.js.gz +0 -0
  357. package/dist/dashboard/assets/numbers-DrdA9oSM.js.gz +0 -0
  358. package/dist/dashboard/assets/query-keys-BFhGpwR1.js.gz +0 -0
  359. package/dist/dashboard/assets/routing-CAb4F37R.js.gz +0 -0
  360. package/dist/dashboard/assets/select-CxVVB__N.js.gz +0 -0
  361. package/dist/dashboard/assets/settings-DNX9sydi.js.gz +0 -0
  362. package/dist/dashboard/assets/stack-uPOwM9Hs.js.gz +0 -0
  363. package/dist/dashboard/assets/state-D1Yz79iL.js.gz +0 -0
  364. package/dist/dashboard/assets/switch-sevsRrRo.js.gz +0 -0
  365. package/dist/dashboard/assets/useMutation-Dem0lXQj.js.gz +0 -0
  366. package/src/observability/event-compaction.ts +0 -317
  367. package/src/observability/event-day-index.ts +0 -439
@@ -2,6 +2,11 @@
2
2
  import { and, asc, eq, inArray, isNull, or, sql, type SQL } from "drizzle-orm";
3
3
  import { ConsoleDomainError } from "../../shared/errors";
4
4
  import { parseCustomProviderId, isBundledProviderId } from "../../../providers/provider-contracts";
5
+ import {
6
+ asRecord,
7
+ firstNonEmptyString as firstString,
8
+ tryParseJson as safeParseJson,
9
+ } from "../../../shared/coerce";
5
10
  import type { ModelDefinition } from "../../../providers/provider-model-definition";
6
11
  import type { ProviderRegistry } from "../../../providers/provider-registry";
7
12
  import { BUNDLED_PROVIDER_MODULES } from "../../../providers/default-registry";
@@ -10,8 +15,7 @@ import { globalOrOwnedBy, ownedByOnly } from "../../../persistence/tenant-scope"
10
15
  import { nextSortIndex, reorderSortIndex } from "../../../persistence/sort-order";
11
16
  import type { CartethyiaDatabase } from "../../../persistence/db-handle";
12
17
  import { buddyTaskLedger, models, providerAccounts, providerOauthStates, providers, telemetryUsageTotals, tenantDisabledModels } from "../../../persistence/schema";
13
- import { queryTelemetryEvents } from "../../../observability/event-log";
14
- import { isGatewayError } from "../../../observability/telemetry-status";
18
+ import { breakdownTelemetry } from "../../../observability/event-query";
15
19
  import type { WireFamily } from "../../../protocol/canonical-model";
16
20
  import { listAccountHealthEvents, recoverAccount, deleteAccountHealthEvents, type AccountHealthEventRecord } from "../../../providers/operations/account-health-service";
17
21
  import { invalidateCredentialCache } from "../../../providers/operations/provider-credential-service";
@@ -39,14 +43,6 @@ const OAUTH_ACCESS_FIELDS = ["accessToken", "access_token", "access", "token"] a
39
43
  /** Field names a pasted OAuth credential may carry its refresh token under. */
40
44
  const OAUTH_REFRESH_FIELDS = ["refreshToken", "refresh_token", "refresh"] as const;
41
45
 
42
- function firstString(record: Record<string, unknown>, fields: readonly string[]): string | undefined {
43
- for (const field of fields) {
44
- const value = record[field];
45
- if (typeof value === "string" && value.trim().length > 0) return value.trim();
46
- }
47
- return undefined;
48
- }
49
-
50
46
  /** Parses an expiry field that may be an ISO string, an epoch ms, or epoch seconds. */
51
47
  function parseExpiry(value: unknown): Date | undefined {
52
48
  if (typeof value === "number" && Number.isFinite(value)) {
@@ -203,12 +199,6 @@ function pastedAuthState(
203
199
  };
204
200
  }
205
201
 
206
- function asRecord(value: unknown): Record<string, unknown> | undefined {
207
- return value !== null && typeof value === "object" && !Array.isArray(value)
208
- ? (value as Record<string, unknown>)
209
- : undefined;
210
- }
211
-
212
202
  /**
213
203
  * Adds or removes the `image` input modality on a stored modalities blob.
214
204
  *
@@ -235,15 +225,6 @@ export function withImageModality(stored: string | null | undefined, image: bool
235
225
  });
236
226
  }
237
227
 
238
- function safeParseJson(value: string | null | undefined): unknown {
239
- if (typeof value !== "string" || value.length === 0) return undefined;
240
- try {
241
- return JSON.parse(value) as unknown;
242
- } catch {
243
- return undefined;
244
- }
245
- }
246
-
247
228
  /** Normalizes a text column read as `unknown` back to a string or null. */
248
229
  function asStoredText(value: unknown): string | null {
249
230
  return typeof value === "string" ? value : null;
@@ -957,17 +938,23 @@ export class DrizzleProviderCatalogStore implements ProviderCatalogStore {
957
938
  const todayStart = new Date();
958
939
  todayStart.setUTCHours(0, 0, 0, 0);
959
940
  const accountIds = rows.map((row) => row.id);
960
- const todayEvents = await queryTelemetryEvents({ tenantId, accountIds, since: todayStart });
961
- const todayByAccount = new Map<string, { requests: number; errors: number; inputTokens: number; outputTokens: number }>();
962
- for (const event of todayEvents) {
963
- if (!event.accountId) continue;
964
- const bucket = todayByAccount.get(event.accountId) ?? { requests: 0, errors: 0, inputTokens: 0, outputTokens: 0 };
965
- bucket.requests += 1;
966
- if (isGatewayError(event.status, event.httpStatus)) bucket.errors += 1;
967
- bucket.inputTokens += event.inputTokens ?? 0;
968
- bucket.outputTokens += event.outputTokens ?? 0;
969
- todayByAccount.set(event.accountId, bucket);
970
- }
941
+ // Today's per-account usage is one GROUP BY, not a read of every event
942
+ // since midnight. `breakdownTelemetry("account")` is capped at 100 groups;
943
+ // a page of accounts never exceeds it, and the lookup below is by id, so a
944
+ // group that fell outside the cap simply reads as "no usage today" — the
945
+ // same value an account with no traffic gets.
946
+ const todayGroups = await breakdownTelemetry(this.db, tenantId, "account", todayStart);
947
+ const todayByAccount = new Map(
948
+ todayGroups.map((group) => [
949
+ group.key,
950
+ {
951
+ requests: group.requests,
952
+ errors: group.errors,
953
+ inputTokens: group.inputTokens,
954
+ outputTokens: group.outputTokens,
955
+ },
956
+ ]),
957
+ );
971
958
  const [lifetimeRows] = await Promise.all([
972
959
  this.db
973
960
  .select()
@@ -1,52 +1,52 @@
1
- import type { ConsoleAccessResolver } from "../auth/access";
2
- import type { AuditRecorder } from "../auth/audit-recorder";
3
- import { markClineApiKeyCredential } from "../../providers/integrations/cline/cline-quota";
4
- import { type QuotaRefreshDeps } from "./refresh";
5
- import type { AccountTestEgressDeps } from "./account-test-egress";
6
-
7
- /**
8
- * Dependencies shared by the tenant-scoped and global-admin account/quota route
9
- * groups. Both groups are composed onto one Elysia instance by
10
- * `createAccountQuotaRoutes`.
11
- */
12
- export interface AccountQuotaRoutesDeps extends QuotaRefreshDeps {
13
- readonly poolSelector?: AccountTestEgressDeps["poolSelector"];
14
- readonly networkBindingFactory?: AccountTestEgressDeps["networkBindingFactory"];
15
- readonly accessResolver: ConsoleAccessResolver;
16
- readonly snapshotInvalidator?: { invalidate(): Promise<number> };
17
- /** Records privileged account mutations (e.g. hard deletion) to `admin_audit_log`. */
18
- readonly auditRecorder?: AuditRecorder | undefined;
19
- }
20
-
21
- export const ACCOUNT_STATUSES = ["active", "cooldown", "disabled"] as const;
22
- export type AccountStatus = (typeof ACCOUNT_STATUSES)[number];
23
-
24
- /**
25
- * Wraps a stored api_key credential in the envelope its collector expects.
26
- *
27
- * Exported because every refresh path needs it, and the ones that omit it
28
- * fail in a way that looks like the credential is dead: an unmarked `cline`
29
- * `sk_...` key takes the OAuth branch, gets `workos:` prepended, and is
30
- * refused with a message naming a version problem. The scheduled sweep once
31
- * omitted it, so a working account was cooled with `quota_fetch_failed` every
32
- * minute while refreshing fine from the console.
33
- */
34
- export function markProviderApiKeyCredential(providerId: string, credential: string): string {
35
- return providerId === "cline" ? markClineApiKeyCredential(credential) : credential;
36
- }
37
-
38
- /**
39
- * Builds the refresh dependency bundle from the route deps, binding the
40
- * provider-specific credential normalisation (`cline` API keys) once so every
41
- * caller refreshes through the same wiring.
42
- */
43
- export function createQuotaRefreshDeps(deps: AccountQuotaRoutesDeps): QuotaRefreshDeps {
44
- return {
45
- db: deps.db,
46
- redis: deps.redis,
47
- providerRegistry: deps.providerRegistry,
48
- resolveCredential: deps.resolveCredential,
49
- ...(deps.snapshotInvalidator ? { snapshotInvalidator: deps.snapshotInvalidator } : {}),
50
- markApiKeyCredential: markProviderApiKeyCredential,
51
- };
52
- }
1
+ import type { ConsoleAccessResolver } from "../auth/access";
2
+ import type { AuditRecorder } from "../auth/audit-recorder";
3
+ import { markClineApiKeyCredential } from "../../providers/integrations/cline/cline-quota";
4
+ import { type QuotaRefreshDeps } from "./refresh";
5
+ import type { AccountTestEgressDeps } from "./account-test-egress";
6
+
7
+ /**
8
+ * Dependencies shared by the tenant-scoped and global-admin account/quota route
9
+ * groups. Both groups are composed onto one Elysia instance by
10
+ * `createAccountQuotaRoutes`.
11
+ */
12
+ export interface AccountQuotaRoutesDeps extends QuotaRefreshDeps {
13
+ readonly poolSelector?: AccountTestEgressDeps["poolSelector"];
14
+ readonly networkBindingFactory?: AccountTestEgressDeps["networkBindingFactory"];
15
+ readonly accessResolver: ConsoleAccessResolver;
16
+ readonly snapshotInvalidator?: { invalidate(): Promise<number> };
17
+ /** Records privileged account mutations (e.g. hard deletion) to `admin_audit_log`. */
18
+ readonly auditRecorder?: AuditRecorder | undefined;
19
+ }
20
+
21
+ export const ACCOUNT_STATUSES = ["active", "cooldown", "disabled"] as const;
22
+ export type AccountStatus = (typeof ACCOUNT_STATUSES)[number];
23
+
24
+ /**
25
+ * Wraps a stored api_key credential in the envelope its collector expects.
26
+ *
27
+ * Exported because every refresh path needs it, and the ones that omit it
28
+ * fail in a way that looks like the credential is dead: an unmarked `cline`
29
+ * `sk_...` key takes the OAuth branch, gets `workos:` prepended, and is
30
+ * refused with a message naming a version problem. The scheduled sweep once
31
+ * omitted it, so a working account was cooled with `quota_fetch_failed` every
32
+ * minute while refreshing fine from the console.
33
+ */
34
+ export function markProviderApiKeyCredential(providerId: string, credential: string): string {
35
+ return providerId === "cline" ? markClineApiKeyCredential(credential) : credential;
36
+ }
37
+
38
+ /**
39
+ * Builds the refresh dependency bundle from the route deps, binding the
40
+ * provider-specific credential normalisation (`cline` API keys) once so every
41
+ * caller refreshes through the same wiring.
42
+ */
43
+ export function createQuotaRefreshDeps(deps: AccountQuotaRoutesDeps): QuotaRefreshDeps {
44
+ return {
45
+ db: deps.db,
46
+ redis: deps.redis,
47
+ providerRegistry: deps.providerRegistry,
48
+ resolveCredential: deps.resolveCredential,
49
+ ...(deps.snapshotInvalidator ? { snapshotInvalidator: deps.snapshotInvalidator } : {}),
50
+ markApiKeyCredential: markProviderApiKeyCredential,
51
+ };
52
+ }
@@ -115,7 +115,6 @@ async function runAccountCheckin(
115
115
  }
116
116
 
117
117
  /** Bound on accounts one manual daily-growth batch may cover. */
118
- const DAILY_GROWTH_MAX_IDS = 100;
119
118
  /** Concurrent accounts inside one manual daily-growth batch. */
120
119
  const DAILY_GROWTH_CONCURRENCY = 3;
121
120
 
@@ -253,8 +252,17 @@ function checkinStateMessage(result: DailyCheckinResult): string {
253
252
  }
254
253
  }
255
254
 
255
+ /**
256
+ * How many accounts are refreshed at once.
257
+ *
258
+ * This is the bound, not a cap on how many may be requested. A tenant with
259
+ * several hundred accounts used to be refused outright at 100 — "accountIds
260
+ * exceeds the limit of 100" — so refreshing the whole fleet meant hand-batching
261
+ * in the UI. Concurrency is what actually protects the upstream and the
262
+ * gateway, and it does so regardless of how many ids arrive, so the request is
263
+ * accepted at any size and drained through this many workers.
264
+ */
256
265
  const QUOTA_REFRESH_CONCURRENCY = 5;
257
- const QUOTA_REFRESH_MAX_IDS = 100;
258
266
 
259
267
  /**
260
268
  * Registers the tenant-scoped account/quota routes onto `app`. Every route here
@@ -547,13 +555,6 @@ export function registerAccountQuotaTenantRoutes(
547
555
  set.status = 400;
548
556
  return { error: "accountIds must be a non-empty array", code: "invalid_request" };
549
557
  }
550
- if (ids.length > QUOTA_REFRESH_MAX_IDS) {
551
- set.status = 400;
552
- return {
553
- error: `accountIds exceeds the limit of ${QUOTA_REFRESH_MAX_IDS}`,
554
- code: "invalid_request",
555
- };
556
- }
557
558
  const accounts = await db
558
559
  .select({
559
560
  accountId: providerAccounts.id,
@@ -668,16 +669,11 @@ export function registerAccountQuotaTenantRoutes(
668
669
  active: t.Boolean(),
669
670
  }),
670
671
  },
671
- async ({ request, body, set }) => {
672
+ async ({ request, body }) => {
672
673
  const access = requireTenantScope(accessResolver(request), "dashboard:write");
674
+ // No size cap: a tenant-wide enable/disable is a legitimate single
675
+ // request, and the UPDATE is one statement over `inArray` either way.
673
676
  if (body.ids.length === 0) return { ok: true, updated: 0 };
674
- if (body.ids.length > QUOTA_REFRESH_MAX_IDS) {
675
- set.status = 400;
676
- return {
677
- error: `ids exceeds the limit of ${QUOTA_REFRESH_MAX_IDS}`,
678
- code: "invalid_request",
679
- };
680
- }
681
677
  const status = body.active ? ("active" as const) : ("disabled" as const);
682
678
  const updated = await db
683
679
  .update(providerAccounts)
@@ -780,13 +776,9 @@ export function registerAccountQuotaTenantRoutes(
780
776
  code: "not_found",
781
777
  };
782
778
  }
783
- if (accounts.length > DAILY_GROWTH_MAX_IDS) {
784
- set.status = 400;
785
- return {
786
- error: `accountIds exceeds the limit of ${DAILY_GROWTH_MAX_IDS}`,
787
- code: "invalid_request",
788
- };
789
- }
779
+ // No size cap, for the same reason as the quota refresh: the work is
780
+ // already drained through a bounded worker pool, so refusing the request
781
+ // protected nothing and just forced the operator to batch by hand.
790
782
  // One egress rotator for the whole request, exactly as the sweep builds
791
783
  // it, so consecutive accounts leave from consecutive pools.
792
784
  const rotator =