cartethyia 2.4.4 → 2.4.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 (304) hide show
  1. package/.env.example +4 -1
  2. package/README.md +18 -0
  3. package/dist/dashboard/assets/AccountCooldown-ChpdzxRH.js.gz +0 -0
  4. package/dist/dashboard/assets/{BannedPage-TDue2X0p.js → BannedPage-QhvbJB9o.js} +1 -1
  5. package/dist/dashboard/assets/BannedPage-QhvbJB9o.js.gz +0 -0
  6. package/dist/dashboard/assets/{CliToolDetailPage-qaHXuiI5.js → CliToolDetailPage-Bcy150Jo.js} +1 -1
  7. package/dist/dashboard/assets/CliToolDetailPage-Bcy150Jo.js.gz +0 -0
  8. package/dist/dashboard/assets/{CliToolsPage-BFuQBKez.js → CliToolsPage-DyNXefph.js} +1 -1
  9. package/dist/dashboard/assets/CliToolsPage-DyNXefph.js.gz +0 -0
  10. package/dist/dashboard/assets/CombosPage-Cak0ljnO.js +1 -0
  11. package/dist/dashboard/assets/CombosPage-Cak0ljnO.js.gz +0 -0
  12. package/dist/dashboard/assets/ConsoleLogPage-CATpAEOz.js +1 -0
  13. package/dist/dashboard/assets/ConsoleLogPage-CATpAEOz.js.gz +0 -0
  14. package/dist/dashboard/assets/{CustomizationPage-BhglcqyR.js → CustomizationPage-D6jEU7nL.js} +1 -1
  15. package/dist/dashboard/assets/CustomizationPage-D6jEU7nL.js.gz +0 -0
  16. package/dist/dashboard/assets/HealthEventsModal-CZkpn0a7.js +1 -0
  17. package/dist/dashboard/assets/HealthEventsModal-CZkpn0a7.js.gz +0 -0
  18. package/dist/dashboard/assets/{LoginPage-CeqROJrT.js → LoginPage-BJlMVN4U.js} +1 -1
  19. package/dist/dashboard/assets/LoginPage-BJlMVN4U.js.gz +0 -0
  20. package/dist/dashboard/assets/ModelPicker-Br3CXEau.js +1 -0
  21. package/dist/dashboard/assets/ModelPicker-Br3CXEau.js.gz +0 -0
  22. package/dist/dashboard/assets/{OverviewPage-CRXSAITY.js → OverviewPage-CQxXbo2g.js} +1 -1
  23. package/dist/dashboard/assets/OverviewPage-CQxXbo2g.js.gz +0 -0
  24. package/dist/dashboard/assets/ProviderDetailPage--0RBNh99.js +3 -0
  25. package/dist/dashboard/assets/ProviderDetailPage--0RBNh99.js.gz +0 -0
  26. package/dist/dashboard/assets/ProviderIcon-paOCq8I0.js.gz +0 -0
  27. package/dist/dashboard/assets/ProvidersPage-1M-C3mbu.js +1 -0
  28. package/dist/dashboard/assets/ProvidersPage-1M-C3mbu.js.gz +0 -0
  29. package/dist/dashboard/assets/ProxyPage-CXiumA53.js +7 -0
  30. package/dist/dashboard/assets/ProxyPage-CXiumA53.js.gz +0 -0
  31. package/dist/dashboard/assets/QuotaPage-BGDIfIYP.js +1 -0
  32. package/dist/dashboard/assets/QuotaPage-BGDIfIYP.js.gz +0 -0
  33. package/dist/dashboard/assets/{SettingsPage-YxRml8AT.js → SettingsPage-BHW4Zt06.js} +1 -1
  34. package/dist/dashboard/assets/SettingsPage-BHW4Zt06.js.gz +0 -0
  35. package/dist/dashboard/assets/SetupPage-VOYC_lIa.js +1 -0
  36. package/dist/dashboard/assets/SetupPage-VOYC_lIa.js.gz +0 -0
  37. package/dist/dashboard/assets/SortableList-DpOTnGuH.js +1 -0
  38. package/dist/dashboard/assets/SortableList-DpOTnGuH.js.gz +0 -0
  39. package/dist/dashboard/assets/StudioPage-BNIDLiU6.js +31 -0
  40. package/dist/dashboard/assets/StudioPage-BNIDLiU6.js.gz +0 -0
  41. package/dist/dashboard/assets/ToolIcon-K1ckcDKg.js.gz +0 -0
  42. package/dist/dashboard/assets/{UsagePage-B6LA4ewF.js → UsagePage-BAFaSDQV.js} +14 -14
  43. package/dist/dashboard/assets/UsagePage-BAFaSDQV.js.gz +0 -0
  44. package/dist/dashboard/assets/api-keys-BETfaMYZ.js +1 -0
  45. package/dist/dashboard/assets/api-keys-BETfaMYZ.js.gz +0 -0
  46. package/dist/dashboard/assets/app-BLCvGdzb.css +2 -0
  47. package/dist/dashboard/assets/app-BLCvGdzb.css.gz +0 -0
  48. package/dist/dashboard/assets/{app-BDJWMThu.js → app-Cjq7nrwu.js} +3 -3
  49. package/dist/dashboard/assets/app-Cjq7nrwu.js.gz +0 -0
  50. package/dist/dashboard/assets/arrow-left-CpBxkoxI.js.gz +0 -0
  51. package/dist/dashboard/assets/arrow-right-Dl5yKnp8.js.gz +0 -0
  52. package/dist/dashboard/assets/{assert-Bwh6DJTK.js → assert-7J6_LVD3.js} +1 -1
  53. package/dist/dashboard/assets/assert-7J6_LVD3.js.gz +0 -0
  54. package/dist/dashboard/assets/backup-file-gdKIlg8U.js.gz +0 -0
  55. package/dist/dashboard/assets/{badge-BwmwEOgA.js → badge-BTyGk_DL.js} +1 -1
  56. package/dist/dashboard/assets/badge-BTyGk_DL.js.gz +0 -0
  57. package/dist/dashboard/assets/{banner-BKBfKTqn.js → banner-bujTWYM4.js} +1 -1
  58. package/dist/dashboard/assets/banner-bujTWYM4.js.gz +0 -0
  59. package/dist/dashboard/assets/brain-Cwed_72z.js.gz +0 -0
  60. package/dist/dashboard/assets/{card-BTdbRFFg.js → card-B0Bnx5CA.js} +1 -1
  61. package/dist/dashboard/assets/card-B0Bnx5CA.js.gz +0 -0
  62. package/dist/dashboard/assets/check-D69FzdvC.js.gz +0 -0
  63. package/dist/dashboard/assets/chevron-right-BXIy5l3h.js.gz +0 -0
  64. package/dist/dashboard/assets/circle-alert-CntYHV5v.js.gz +0 -0
  65. package/dist/dashboard/assets/circle-check-BU3v_mFW.js.gz +0 -0
  66. package/dist/dashboard/assets/cli-tools-DE2ug812.js +1 -0
  67. package/dist/dashboard/assets/cli-tools-DE2ug812.js.gz +0 -0
  68. package/dist/dashboard/assets/clipboard-button-R_rArH8S.js +1 -0
  69. package/dist/dashboard/assets/clipboard-button-R_rArH8S.js.gz +0 -0
  70. package/dist/dashboard/assets/cog-CdWhsajs.js.gz +0 -0
  71. package/dist/dashboard/assets/copy-ngmQ9zcZ.js.gz +0 -0
  72. package/dist/dashboard/assets/{dialog-iocKA3jr.js → dialog-BWIP_EvW.js} +1 -1
  73. package/dist/dashboard/assets/dialog-BWIP_EvW.js.gz +0 -0
  74. package/dist/dashboard/assets/dialog-actions-B1axpEg7.js.gz +0 -0
  75. package/dist/dashboard/assets/download-A0SW9OCW.js.gz +0 -0
  76. package/dist/dashboard/assets/download-dRFo5_Wu.js.gz +0 -0
  77. package/dist/dashboard/assets/{drawer-CDKOTZdY.js → drawer-BKcgVugF.js} +1 -1
  78. package/dist/dashboard/assets/drawer-BKcgVugF.js.gz +0 -0
  79. package/dist/dashboard/assets/entry-7dpgvpJ3.js +4 -0
  80. package/dist/dashboard/assets/entry-7dpgvpJ3.js.gz +0 -0
  81. package/dist/dashboard/assets/entry-CcWkGR4a.js +1 -0
  82. package/dist/dashboard/assets/entry-CcWkGR4a.js.gz +0 -0
  83. package/dist/dashboard/assets/entry-kTiTFrvh.js +1 -0
  84. package/dist/dashboard/assets/entry-kTiTFrvh.js.gz +0 -0
  85. package/dist/dashboard/assets/eye-C_hawfH4.js.gz +0 -0
  86. package/dist/dashboard/assets/eye-off-Bk5g2luF.js.gz +0 -0
  87. package/dist/dashboard/assets/fingerprint-pattern-BWuHtbW4.js.gz +0 -0
  88. package/dist/dashboard/assets/gauge-BbIMy7MB.js.gz +0 -0
  89. package/dist/dashboard/assets/github-badge-Booc5mNe.js +1 -0
  90. package/dist/dashboard/assets/github-badge-Booc5mNe.js.gz +0 -0
  91. package/dist/dashboard/assets/image-BPaouViG.js.gz +0 -0
  92. package/dist/dashboard/assets/{index-BA_ni9Ix.js → index-D70pChfn.js} +2 -2
  93. package/dist/dashboard/assets/index-D70pChfn.js.gz +0 -0
  94. package/dist/dashboard/assets/{inline-DCejSLlA.js → inline-D1TCqM2D.js} +1 -1
  95. package/dist/dashboard/assets/inline-D1TCqM2D.js.gz +0 -0
  96. package/dist/dashboard/assets/{input-Bn4_kNav.js → input-B9oyU93y.js} +1 -1
  97. package/dist/dashboard/assets/input-B9oyU93y.js.gz +0 -0
  98. package/dist/dashboard/assets/jsx-runtime-CXUMngI_.js.gz +0 -0
  99. package/dist/dashboard/assets/key-round-UkavsGxp.js.gz +0 -0
  100. package/dist/dashboard/assets/layers-2-glBbjl.js.gz +0 -0
  101. package/dist/dashboard/assets/{layout-Bv-NxqZ5.js → layout-BArzXirQ.js} +1 -1
  102. package/dist/dashboard/assets/layout-BArzXirQ.js.gz +0 -0
  103. package/dist/dashboard/assets/{list-row-BLA0sldD.js → list-row-BVZzaoHF.js} +1 -1
  104. package/dist/dashboard/assets/list-row-BVZzaoHF.js.gz +0 -0
  105. package/dist/dashboard/assets/{live-YlifPYox.js → live-EmuIayqp.js} +1 -1
  106. package/dist/dashboard/assets/live-EmuIayqp.js.gz +0 -0
  107. package/dist/dashboard/assets/log-in-BmRjy60c.js.gz +0 -0
  108. package/dist/dashboard/assets/message-circle-CCxGwRvv.js.gz +0 -0
  109. package/dist/dashboard/assets/{model-bans-CVvHZ-qz.js → model-bans-CSPQ9xB0.js} +1 -1
  110. package/dist/dashboard/assets/model-bans-CSPQ9xB0.js.gz +0 -0
  111. package/dist/dashboard/assets/mutation-BkoLM5Ba.js +1 -0
  112. package/dist/dashboard/assets/mutation-BkoLM5Ba.js.gz +0 -0
  113. package/dist/dashboard/assets/numbers-CFL4_Dqc.js.gz +0 -0
  114. package/dist/dashboard/assets/pencil-768ysRAr.js.gz +0 -0
  115. package/dist/dashboard/assets/plus-DIdX_g-x.js.gz +0 -0
  116. package/dist/dashboard/assets/power-off-Yjw9GMJh.js +1 -0
  117. package/dist/dashboard/assets/power-off-Yjw9GMJh.js.gz +0 -0
  118. package/dist/dashboard/assets/privacy-mode-GM8bej85.js +1 -0
  119. package/dist/dashboard/assets/privacy-mode-GM8bej85.js.gz +0 -0
  120. package/dist/dashboard/assets/providers/agentrouter.svg.gz +0 -0
  121. package/dist/dashboard/assets/providers/aihubmix.svg.gz +0 -0
  122. package/dist/dashboard/assets/providers/alibaba-coding-plan.svg.gz +0 -0
  123. package/dist/dashboard/assets/providers/alibaba.svg.gz +0 -0
  124. package/dist/dashboard/assets/providers/antigravity.svg.gz +0 -0
  125. package/dist/dashboard/assets/providers/bai.svg.gz +0 -0
  126. package/dist/dashboard/assets/providers/blackbox.svg.gz +0 -0
  127. package/dist/dashboard/assets/providers/claude-code.svg.gz +0 -0
  128. package/dist/dashboard/assets/providers/exa.svg.gz +0 -0
  129. package/dist/dashboard/assets/providers/gmi-cloud.svg.gz +0 -0
  130. package/dist/dashboard/assets/providers/inferhub.svg.gz +0 -0
  131. package/dist/dashboard/assets/providers/muse.svg.gz +0 -0
  132. package/dist/dashboard/assets/providers/nous-research.svg.gz +0 -0
  133. package/dist/dashboard/assets/providers/openai-light.svg.gz +0 -0
  134. package/dist/dashboard/assets/providers/parallel.svg.gz +0 -0
  135. package/dist/dashboard/assets/providers/tokenharbor.svg.gz +0 -0
  136. package/dist/dashboard/assets/providers/zai-coding-plan.svg.gz +0 -0
  137. package/dist/dashboard/assets/{query-keys-ROf-g1Wi.js → query-keys-D7wX2vM5.js} +1 -1
  138. package/dist/dashboard/assets/query-keys-D7wX2vM5.js.gz +0 -0
  139. package/dist/dashboard/assets/quota-DZGyp0N5.js.gz +0 -0
  140. package/dist/dashboard/assets/refresh-cw-BqJFk4sU.js.gz +0 -0
  141. package/dist/dashboard/assets/rotate-ccw-CufcqITi.js.gz +0 -0
  142. package/dist/dashboard/assets/route-CNSNj9Zm.js.gz +0 -0
  143. package/dist/dashboard/assets/{routing-jntw9-Ui.js → routing-BExgxxBU.js} +1 -1
  144. package/dist/dashboard/assets/routing-BExgxxBU.js.gz +0 -0
  145. package/dist/dashboard/assets/scaling-nWE4Cx8f.js.gz +0 -0
  146. package/dist/dashboard/assets/search-JOKus9rb.js.gz +0 -0
  147. package/dist/dashboard/assets/{select-CGUuTQmj.js → select-C9mMoEpJ.js} +1 -1
  148. package/dist/dashboard/assets/select-C9mMoEpJ.js.gz +0 -0
  149. package/dist/dashboard/assets/settings-C4_Gn_wq.js +1 -0
  150. package/dist/dashboard/assets/settings-C4_Gn_wq.js.gz +0 -0
  151. package/dist/dashboard/assets/shield-check-DVS0Foqh.js.gz +0 -0
  152. package/dist/dashboard/assets/sliders-horizontal-ClG7dBeJ.js.gz +0 -0
  153. package/dist/dashboard/assets/sparkles-BRaRUxAU.js.gz +0 -0
  154. package/dist/dashboard/assets/{stack-C1kTcOmP.js → stack-Ca5HxI5i.js} +1 -1
  155. package/dist/dashboard/assets/stack-Ca5HxI5i.js.gz +0 -0
  156. package/dist/dashboard/assets/{state-DHKdg-pm.js → state-CzUPfLXo.js} +1 -1
  157. package/dist/dashboard/assets/state-CzUPfLXo.js.gz +0 -0
  158. package/dist/dashboard/assets/stream-retry-5QnVuSSo.js.gz +0 -0
  159. package/dist/dashboard/assets/{switch-DIDeAiz9.js → switch-DOUIky_i.js} +1 -1
  160. package/dist/dashboard/assets/switch-DOUIky_i.js.gz +0 -0
  161. package/dist/dashboard/assets/terminal-Bd4ee8TA.js +1 -0
  162. package/dist/dashboard/assets/terminal-Bd4ee8TA.js.gz +0 -0
  163. package/dist/dashboard/assets/toast-C76WrMhc.js +2 -0
  164. package/dist/dashboard/assets/toast-C76WrMhc.js.gz +0 -0
  165. package/dist/dashboard/assets/tokens-DDxMbSRL.js.gz +0 -0
  166. package/dist/dashboard/assets/trash-Cppn5c3R.js.gz +0 -0
  167. package/dist/dashboard/assets/use-clipboard-B0Sy03fE.js.gz +0 -0
  168. package/dist/dashboard/assets/use-presence-BSZEBx7U.js.gz +0 -0
  169. package/dist/dashboard/assets/use-reduced-motion-C4B0R6Sz.js.gz +0 -0
  170. package/dist/dashboard/assets/use-timeout-BwcBBRrT.js.gz +0 -0
  171. package/dist/dashboard/assets/{useMutation-DVmwOKU-.js → useMutation-D87FZwH0.js} +1 -1
  172. package/dist/dashboard/assets/useMutation-D87FZwH0.js.gz +0 -0
  173. package/dist/dashboard/assets/wrench-B6E1oEuY.js.gz +0 -0
  174. package/dist/dashboard/index.html +1 -1
  175. package/dist/dashboard/index.html.gz +0 -0
  176. package/dist/dashboard/precompressed-assets.json +123 -0
  177. package/migrations/full-postgres/0013_add_providers_tenant_id_index.sql +22 -0
  178. package/migrations/full-postgres/0014_add_provider_accounts_tenant_id_index.sql +21 -0
  179. package/migrations/lite-sqlite3/0000_baseline.sql +4 -2
  180. package/migrations/lite-sqlite3/0013_add_providers_tenant_id_index.sql +4 -0
  181. package/migrations/lite-sqlite3/0014_add_provider_accounts_tenant_id_index.sql +4 -0
  182. package/package.json +25 -30
  183. package/scripts/cli/{carte-launcher.cjs → bin/carte.cjs} +8 -8
  184. package/scripts/cli/{cli-launcher.cjs → bin/cartethyia.cjs} +167 -134
  185. package/scripts/cli/command/admin.ts +65 -0
  186. package/scripts/cli/command/console-menu.ts +227 -0
  187. package/scripts/cli/{doctor.ts → command/doctor.ts} +257 -258
  188. package/scripts/cli/{setup.ts → command/setup.ts} +8 -12
  189. package/scripts/cli/command/start.ts +13 -0
  190. package/scripts/cli/command/tray.ts +165 -0
  191. package/scripts/cli/command/update.ts +23 -0
  192. package/scripts/cli/{runtime-state.ts → gateway/drain-token.ts} +78 -78
  193. package/scripts/cli/gateway/spawn.ts +101 -0
  194. package/scripts/cli/gateway/stop.ts +51 -0
  195. package/scripts/cli/main.ts +94 -0
  196. package/scripts/cli/support/bun.ts +27 -0
  197. package/scripts/cli/support/env.ts +99 -0
  198. package/scripts/cli/support/platform.ts +64 -0
  199. package/scripts/cli/support/services.ts +88 -0
  200. package/scripts/cli/support/startup-signal.ts +23 -0
  201. package/scripts/cli/support/version.ts +37 -0
  202. package/scripts/cli/tray/hidden-window.vbs +28 -0
  203. package/scripts/cli/{tray.ts → tray/index.ts} +3 -3
  204. package/scripts/cli/{tray.ps1 → tray/windows.ps1} +107 -98
  205. package/scripts/cli/ui/background.ts +49 -0
  206. package/src/boot/app.ts +3 -1
  207. package/src/boot/dependencies.ts +36 -13
  208. package/src/config/providers.ts +27 -1
  209. package/src/config/spec.ts +11 -0
  210. package/src/console/backup/routes.ts +8 -0
  211. package/src/console/cli-tools/contracts.ts +6 -6
  212. package/src/console/console-router.ts +5 -3
  213. package/src/console/dashboard-assets.ts +81 -11
  214. package/src/console/observability/contracts.ts +54 -22
  215. package/src/console/observability/routes.ts +31 -6
  216. package/src/console/observability/store.ts +68 -28
  217. package/src/console/providers/catalog/public-model-store.ts +91 -60
  218. package/src/console/providers/catalog/store.ts +5 -1
  219. package/src/console/quota/account-quota-tenant.ts +9 -6
  220. package/src/console/response-compression.ts +69 -2
  221. package/src/console/routing/pools/probe-verdict.ts +72 -0
  222. package/src/console/share/share-router.ts +7 -4
  223. package/src/console/share/share-stats.ts +12 -5
  224. package/src/console/shared/read-through-cache.ts +133 -7
  225. package/src/console/updates/repository-updates.ts +39 -34
  226. package/src/network/outbound-fetch.ts +113 -25
  227. package/src/network/tls/bun-client-hello.ts +112 -0
  228. package/src/observability/event-log.ts +29 -8
  229. package/src/observability/metrics.ts +40 -13
  230. package/src/observability/runtime-metrics.ts +7 -0
  231. package/src/persistence/schema.ts +40 -2
  232. package/src/protocol/response/codex.ts +9 -75
  233. package/src/protocol/tool-identity.ts +43 -9
  234. package/src/providers/authentication/jwt-validator.ts +130 -187
  235. package/src/providers/authentication/oauth-flow-store.ts +16 -9
  236. package/src/providers/default-registry.ts +4 -0
  237. package/src/providers/discovery/catalog/catalog-format.ts +20 -0
  238. package/src/providers/discovery/models-dev-catalog.ts +39 -22
  239. package/src/providers/discovery/probe-wire.ts +29 -5
  240. package/src/providers/integrations/buddy/buddy-catalog-shared.ts +3 -2
  241. package/src/providers/integrations/buddy/buddy-cli-headers.ts +88 -0
  242. package/src/providers/integrations/buddy/buddy-conversation-headers.ts +4 -0
  243. package/src/providers/integrations/buddy/buddy-discovery-shared.ts +33 -1
  244. package/src/providers/integrations/buddy/buddy-identity.ts +46 -0
  245. package/src/providers/integrations/buddy/buddy-oauth-shared.ts +37 -1
  246. package/src/providers/integrations/buddy/buddy-usage-store.ts +40 -5
  247. package/src/providers/integrations/buddy/codebuddy/provider.ts +2 -2
  248. package/src/providers/integrations/buddy/codebuddy/shared.ts +106 -73
  249. package/src/providers/integrations/buddy/workbuddy/cn-provider.ts +5 -2
  250. package/src/providers/integrations/buddy/workbuddy/quota.ts +172 -159
  251. package/src/providers/integrations/buddy/workbuddy/shared.ts +200 -171
  252. package/src/providers/integrations/codex/codex.ts +5 -8
  253. package/src/providers/integrations/factory/factory-oauth.ts +7 -11
  254. package/src/providers/integrations/opencode/opencode-go-quota.ts +130 -0
  255. package/src/providers/integrations/opencode/provider.ts +42 -3
  256. package/src/providers/integrations/qoder/qoder-oauth.ts +3 -9
  257. package/src/providers/integrations/shared/install-id.ts +29 -2
  258. package/src/providers/operations/client-versions.ts +11 -1
  259. package/src/providers/operations/model-catalog-cache.ts +18 -3
  260. package/src/providers/operations/provider-catalog-service.ts +76 -0
  261. package/src/transport/dispatch/attempt-finalize.ts +7 -1
  262. package/src/transport/dispatch/attempt-loop.ts +9 -7
  263. package/src/transport/dispatch/empty-stream.ts +17 -26
  264. package/src/transport/failure-policy.ts +20 -0
  265. package/src/transport/routing/route-catalog.ts +42 -3
  266. package/src/transport/surface/chat/encode.ts +18 -31
  267. package/src/transport/surface/messages/stream.ts +3 -2
  268. package/src/transport/translation/tool-repair.ts +37 -16
  269. package/src/workers/account-identity-sweep.ts +14 -12
  270. package/src/workers/daily-checkin.ts +62 -0
  271. package/src/workers/quota-refresh-sweep.ts +27 -2
  272. package/dist/dashboard/assets/CombosPage-B7ETkfVH.js +0 -1
  273. package/dist/dashboard/assets/ConsoleLogPage-vkZBxMVo.js +0 -1
  274. package/dist/dashboard/assets/HealthEventsModal-DDkHfwpV.js +0 -1
  275. package/dist/dashboard/assets/ModelPicker-CAiPR5Vd.js +0 -1
  276. package/dist/dashboard/assets/ProviderDetailPage-cNAD_6uN.js +0 -3
  277. package/dist/dashboard/assets/ProvidersPage-Cv5tdxlx.js +0 -1
  278. package/dist/dashboard/assets/ProxyPage-CWvMfzjj.js +0 -7
  279. package/dist/dashboard/assets/QuotaPage-IYQOOHR4.js +0 -1
  280. package/dist/dashboard/assets/SetupPage-BPDNo3Ek.js +0 -1
  281. package/dist/dashboard/assets/SortableList-Bol1ASmI.js +0 -1
  282. package/dist/dashboard/assets/StudioPage-BE5DJONZ.js +0 -31
  283. package/dist/dashboard/assets/api-keys-CjIGQhe7.js +0 -1
  284. package/dist/dashboard/assets/app-Cjgo4-Lf.css +0 -2
  285. package/dist/dashboard/assets/cli-tools-DccaOfhX.js +0 -1
  286. package/dist/dashboard/assets/clipboard-button-DQmMikNQ.js +0 -1
  287. package/dist/dashboard/assets/entry-DTiWhIY-.js +0 -4
  288. package/dist/dashboard/assets/entry-Dm3PpDCO.js +0 -1
  289. package/dist/dashboard/assets/entry-keNDZ_Wz.js +0 -1
  290. package/dist/dashboard/assets/github-badge-BuAJr8oU.js +0 -1
  291. package/dist/dashboard/assets/mutation-BIizFZav.js +0 -1
  292. package/dist/dashboard/assets/privacy-mode-CQjbZ3xk.js +0 -1
  293. package/dist/dashboard/assets/settings-BAS6QbmJ.js +0 -1
  294. package/dist/dashboard/assets/terminal-C9mZ4Ugm.js +0 -1
  295. package/dist/dashboard/assets/toast-DKAdP2mo.js +0 -2
  296. package/scripts/cli/cli.ts +0 -766
  297. package/scripts/cli/version.ts +0 -66
  298. package/scripts/operations/probe-egress.ts +0 -335
  299. package/scripts/operations/restart.ts +0 -120
  300. package/scripts/shared/env.ts +0 -269
  301. /package/{tray-assets → scripts/cli/tray/assets}/cartethyia.ico +0 -0
  302. /package/{tray-assets → scripts/cli/tray/assets}/cartethyia.png +0 -0
  303. /package/scripts/cli/{menu.ts → ui/prompt.ts} +0 -0
  304. /package/scripts/cli/{wordmark.ts → ui/wordmark.ts} +0 -0
@@ -169,10 +169,11 @@ interface HistogramSeries {
169
169
  class Histogram implements HistogramMetric {
170
170
  private readonly series = new Map<string, HistogramSeries>();
171
171
  /**
172
- * The label names this histogram emits, fixed by the first LABELLED
173
- * observation. A Prometheus metric family must expose one consistent label set,
174
- * so a later observation carrying different keys is normalized against this set
175
- * rather than creating a second, incompatible series.
172
+ * The label names this histogram emits. Declared at registration when the
173
+ * caller passes them to `histogram()`, otherwise fixed by the first LABELLED
174
+ * observation. A Prometheus metric family must expose one consistent label
175
+ * set, so a later observation carrying different keys is normalized against
176
+ * this set rather than creating a second, incompatible series.
176
177
  */
177
178
  private labelNames: readonly string[] = [];
178
179
 
@@ -180,12 +181,18 @@ class Histogram implements HistogramMetric {
180
181
  readonly name: string,
181
182
  readonly help: string,
182
183
  readonly buckets: readonly number[],
183
- ) {}
184
+ labelNames: readonly string[] = [],
185
+ ) {
186
+ // A declared set is stable across runs and workers, so the exposition is
187
+ // deterministic even before the first observation arrives. Sorting keeps
188
+ // the order fixed either way.
189
+ this.labelNames = [...labelNames].sort();
190
+ }
184
191
 
185
192
  observe(value: number, labels?: MetricLabels): void {
193
+ // Fall back to inferring the set from the first labelled observation when
194
+ // the metric was registered without declaring one.
186
195
  if (this.labelNames.length === 0 && labels !== undefined) {
187
- // Sorting makes the exposition order deterministic, so two runs of the same
188
- // code produce byte-identical output.
189
196
  this.labelNames = Object.keys(labels).sort();
190
197
  }
191
198
  const normalized = normalizeLabels(labels, this.labelNames);
@@ -271,7 +278,8 @@ export class PrometheusRegistry {
271
278
  readonly cartethyia_routing_roundrobin_entries: GaugeMetric;
272
279
  readonly cartethyia_ip_abuse_keys: GaugeMetric;
273
280
  readonly cartethyia_quota_cache_entries: GaugeMetric;
274
- readonly cartethyia_proxy_dial_dns_fallback_total: CounterMetric;
281
+ readonly cartethyia_model_catalog_entries: GaugeMetric;
282
+ readonly cartethyia_proxy_dial_proxy_resolved_total: CounterMetric;
275
283
  readonly proxy_provider_adapter_load_ms: HistogramMetric;
276
284
  readonly pool_cooldown_record_failed: CounterMetric;
277
285
  readonly quota_cache_invalidate_failed: CounterMetric;
@@ -348,9 +356,13 @@ export class PrometheusRegistry {
348
356
  "cartethyia_quota_cache_entries",
349
357
  "Entries in the in-process provider quota cache",
350
358
  );
351
- this.cartethyia_proxy_dial_dns_fallback_total = this.counter(
352
- "cartethyia_proxy_dial_dns_fallback_total",
353
- "Pool/relay-bound dials whose local DNS resolution failed and were downgraded to proxy-resolved egress",
359
+ this.cartethyia_model_catalog_entries = this.gauge(
360
+ "cartethyia_model_catalog_entries",
361
+ "Entries in the per-provider model catalog cache (at the cap means eviction churn)",
362
+ );
363
+ this.cartethyia_proxy_dial_proxy_resolved_total = this.counter(
364
+ "cartethyia_proxy_dial_proxy_resolved_total",
365
+ "Pool/relay-bound dials whose target is resolved by the proxy or relay rather than by the local resolver, so local DNS for the target is advisory only",
354
366
  );
355
367
  this.proxy_provider_adapter_load_ms = this.histogram(
356
368
  "proxy_provider_adapter_load_ms",
@@ -379,8 +391,23 @@ export class PrometheusRegistry {
379
391
  return metric;
380
392
  }
381
393
 
382
- histogram(name: string, help: string, buckets: readonly number[]): HistogramMetric {
383
- const metric = new Histogram(name, help, buckets);
394
+ /**
395
+ * Registers a histogram.
396
+ *
397
+ * `labelNames` declares the label set up front. It is optional because a
398
+ * histogram observed without labels still has to render, but passing it is
399
+ * the correct call: a declared set is stable across runs and workers, so the
400
+ * exposition is deterministic even before the first observation, and an
401
+ * observation carrying undeclared keys cannot create a second, incompatible
402
+ * series.
403
+ */
404
+ histogram(
405
+ name: string,
406
+ help: string,
407
+ buckets: readonly number[],
408
+ labelNames: readonly string[] = [],
409
+ ): HistogramMetric {
410
+ const metric = new Histogram(name, help, buckets, labelNames);
384
411
  this.all.push(metric);
385
412
  return metric;
386
413
  }
@@ -24,6 +24,12 @@ export interface RuntimeMetricSources {
24
24
  readonly ipAbuseKeys: () => number;
25
25
  /** Tracked keys in the in-process quota cache tier (0 when Redis-only paths are idle). */
26
26
  readonly quotaCacheEntries: () => number;
27
+
28
+ /**
29
+ * Entries in the per-provider model-catalog cache. Bounded, but a count that
30
+ * sits at the cap means every rebuild is evicting something it needs.
31
+ */
32
+ readonly modelCatalogEntries: () => number;
27
33
  /**
28
34
  * Live in-flight request count. Zero means the gateway is idle, so a
29
35
  * synchronous full collection is safe (no request to stall).
@@ -129,6 +135,7 @@ export class RuntimeMetricsSampler {
129
135
  metrics.cartethyia_routing_roundrobin_entries.set(roundRobin.provider, { scope: "provider" });
130
136
  metrics.cartethyia_ip_abuse_keys.set(this.sources.ipAbuseKeys());
131
137
  metrics.cartethyia_quota_cache_entries.set(this.sources.quotaCacheEntries());
138
+ metrics.cartethyia_model_catalog_entries.set(this.sources.modelCatalogEntries());
132
139
 
133
140
  this.checkPressure(memory.rss);
134
141
  this.checkIdleReclaim(memory.rss);
@@ -1,5 +1,5 @@
1
1
  /** Canonical Drizzle schema: every persistence table, enum, index, and shared type. */
2
- import { sql } from "drizzle-orm";
2
+ import { desc, sql } from "drizzle-orm";
3
3
  import {
4
4
  bigint, boolean, check, customType, index, integer, jsonb, numeric, pgEnum, pgTable,
5
5
  primaryKey, text, timestamp, uniqueIndex, uuid, type AnyPgColumn,
@@ -168,6 +168,14 @@ export const providers = pgTable(
168
168
  // see `src/transport/routing/route-catalog.ts`.
169
169
  requiresAccount: boolean("requires_account").notNull().default(true),
170
170
  },
171
+ (table) => [
172
+ // `globalOrOwnedBy(providers.tenantId, …)` is the predicate behind
173
+ // `listModelsForTenant` and `/quota/overview`, and it is a bare
174
+ // `tenant_id IS NULL OR tenant_id = ?` — which no other index here can
175
+ // serve, so both reads scanned the whole provider list. Cheap to keep:
176
+ // providers are few and written rarely.
177
+ index("providers_tenant_id_idx").on(table.tenantId),
178
+ ],
171
179
  );
172
180
 
173
181
  // Upstream account credentials plus the full health state machine.
@@ -262,6 +270,12 @@ export const providerAccounts = pgTable("provider_accounts", {
262
270
  ),
263
271
  index("provider_accounts_status_cooldown_idx").on(table.status, table.cooldownUntil),
264
272
  index("provider_accounts_provider_tenant_idx").on(table.providerId, table.tenantId),
273
+ // The bare `tenant_id IS NULL OR tenant_id = ?` that `listAllAccounts`
274
+ // uses is served by neither index above: `(status, cooldown_until)` leads
275
+ // with the wrong column, and `(provider_id, tenant_id)` cannot serve a
276
+ // predicate that does not constrain `provider_id`. This one matches the
277
+ // hot listing path.
278
+ index("provider_accounts_tenant_id_idx").on(table.tenantId),
265
279
  ],
266
280
  );
267
281
 
@@ -473,7 +487,16 @@ export const healthEvents = pgTable(
473
487
  },
474
488
  (table) => [
475
489
  index("health_events_account_id_created_at_idx").on(table.accountId, table.createdAt),
476
- index("health_events_account_model_created_idx").on(table.accountId, table.modelId, table.createdAt),
490
+ // Descending `created_at` is deliberate: health history is always read
491
+ // newest-first. The SQLite baseline drops the direction (see
492
+ // `scripts/generate/sqlite-ddl.ts` — SQLite scans an index either way),
493
+ // so this is a Postgres-only access-path choice, not a cross-backend
494
+ // disagreement.
495
+ index("health_events_account_model_created_idx").on(
496
+ table.accountId,
497
+ table.modelId,
498
+ desc(table.createdAt),
499
+ ),
477
500
  index("health_events_network_pool_id_created_at_idx").on(table.networkPoolId, table.createdAt),
478
501
  check(
479
502
  "health_events_entity_reference_check",
@@ -723,6 +746,21 @@ export const apiKeys = pgTable(
723
746
  },
724
747
  (table) => [
725
748
  uniqueIndex("api_keys_key_hash_idx").on(table.keyHash),
749
+ /**
750
+ * One *live* default key per tenant, enforced by the database.
751
+ *
752
+ * The baseline SQL has always created this; the schema never declared it,
753
+ * so the two descriptions disagreed and nothing noticed. It is the only
754
+ * database-level guarantee behind `isDefault`, and
755
+ * `test/persistence/sqlite-baseline.test.ts` pins it by name — so the
756
+ * schema was the incomplete side, not the migration.
757
+ *
758
+ * Partial on `revoked_at IS NULL` so a tenant can rotate its default key
759
+ * without the old, revoked row blocking the new one.
760
+ */
761
+ uniqueIndex("api_keys_one_default_per_tenant_uidx")
762
+ .on(table.tenantId)
763
+ .where(sql`${table.isDefault} = true AND ${table.revokedAt} IS NULL`),
726
764
  index("api_keys_tenant_id_idx").on(table.tenantId),
727
765
  index("api_keys_enabled_tenant_idx").on(table.tenantId).where(sql`${table.enabled} = true`),
728
766
  index("api_keys_parent_key_id_idx").on(table.parentKeyId),
@@ -281,9 +281,7 @@ export class CodexStreamFrameProcessor {
281
281
  // output_index routing + `output_item.added` registration.
282
282
  readonly #openItems = new Map<string, OpenItemEntry>();
283
283
  readonly #openItemsByOutputIndex = new Map<number, OpenItemEntry>();
284
- // Track tool call argument accumulation, retained for diagnostics and
285
- // future reconciliation of streamed arguments against authoritative ones.
286
- readonly #toolCallArgs = new Map<string, string[]>();
284
+
287
285
  // Whitespace-loop guard: a model stuck emitting only whitespace tool-call
288
286
  // argument deltas would otherwise hang the dispatch loop forever.
289
287
  readonly #whitespaceDeltaTracking = new Map<
@@ -427,9 +425,6 @@ export class CodexStreamFrameProcessor {
427
425
  : undefined;
428
426
  const key = itemId ?? callId;
429
427
  this.#checkWhitespaceLoopGuard(key, json["delta"] as string);
430
- const chunks = this.#toolCallArgs.get(key) ?? [];
431
- chunks.push(json["delta"] as string);
432
- this.#toolCallArgs.set(key, chunks);
433
428
  events.push({
434
429
  type: "tool_call_delta",
435
430
  sequence_number: this.#seq++,
@@ -443,14 +438,7 @@ export class CodexStreamFrameProcessor {
443
438
  typeof json["delta"] === "string"
444
439
  ) {
445
440
  const { callId, name } = this.#resolveToolIdentity(json);
446
- const itemId =
447
- typeof json["item_id"] === "string"
448
- ? (json["item_id"] as string)
449
- : undefined;
450
- const key = itemId ?? callId;
451
- const chunks = this.#toolCallArgs.get(key) ?? [];
452
- chunks.push(json["delta"] as string);
453
- this.#toolCallArgs.set(key, chunks);
441
+
454
442
  events.push({
455
443
  type: "tool_call_delta",
456
444
  sequence_number: this.#seq++,
@@ -505,37 +493,13 @@ export class CodexStreamFrameProcessor {
505
493
  },
506
494
  });
507
495
  } else if (t === "response.function_call_arguments.done") {
508
- const rawCallId =
509
- (json["call_id"] as string | undefined) ??
510
- (json["item_id"] as string | undefined);
511
- const itemId =
512
- typeof json["item_id"] === "string"
513
- ? (json["item_id"] as string)
514
- : undefined;
515
- let key: string | undefined;
516
- if (itemId !== undefined) key = itemId;
517
- else if (rawCallId !== undefined) key = decodeCodexToolCallId(rawCallId);
518
- else {
519
- const outputIndex = readOutputIndex(json);
520
- if (outputIndex !== undefined) key = `idx_${outputIndex}`;
521
- }
522
- const finalArgs = (json as Record<string, unknown>)["arguments"];
523
- if (typeof finalArgs === "string" && key !== undefined) {
524
- this.#toolCallArgs.set(key, [finalArgs]);
525
- }
496
+ // The arguments themselves are emitted by the delta events above; the
497
+ // `.done` event only marks the call complete.
498
+
526
499
  this.hasToolCall = true;
527
500
  } else if (t === "response.custom_tool_call_input.done") {
528
- const itemId =
529
- typeof json["item_id"] === "string"
530
- ? (json["item_id"] as string)
531
- : undefined;
532
- const rawCallId = (json["call_id"] as string | undefined) ?? itemId;
533
- let key: string | undefined;
534
- if (itemId !== undefined) key = itemId;
535
- else if (rawCallId !== undefined) key = decodeCodexToolCallId(rawCallId);
536
- const finalInput = (json as Record<string, unknown>)["input"];
537
- if (typeof finalInput === "string" && key !== undefined)
538
- this.#toolCallArgs.set(key, [finalInput]);
501
+ // As above: the input arrives as deltas, so `.done` only completes it.
502
+
539
503
  this.hasToolCall = true;
540
504
  } else if (t === "response.output_item.done") {
541
505
  const item = json["item"];
@@ -598,43 +562,13 @@ export class CodexStreamFrameProcessor {
598
562
  }
599
563
  if (outputItem["type"] === "function_call") {
600
564
  this.hasToolCall = true;
601
- const callIdRaw =
602
- typeof outputItem["call_id"] === "string"
603
- ? (outputItem["call_id"] as string)
604
- : undefined;
605
- const decodedCallId =
606
- callIdRaw !== undefined
607
- ? decodeCodexToolCallId(callIdRaw)
608
- : undefined;
609
- const authoritative =
610
- typeof outputItem["arguments"] === "string"
611
- ? (outputItem["arguments"] as string)
612
- : undefined;
613
- if (authoritative !== undefined && decodedCallId !== undefined) {
614
- const key = outputItemId ?? decodedCallId;
615
- this.#toolCallArgs.set(key, [authoritative]);
616
- }
565
+
617
566
  if (outputItemId !== undefined) this.#openItems.delete(outputItemId);
618
567
  if (outputIndex !== undefined)
619
568
  this.#openItemsByOutputIndex.delete(outputIndex);
620
569
  } else if (outputItem["type"] === "custom_tool_call") {
621
570
  this.hasToolCall = true;
622
- const callIdRaw =
623
- typeof outputItem["call_id"] === "string"
624
- ? (outputItem["call_id"] as string)
625
- : undefined;
626
- const decodedCallId =
627
- callIdRaw !== undefined
628
- ? decodeCodexToolCallId(callIdRaw)
629
- : undefined;
630
- const authoritative =
631
- typeof outputItem["input"] === "string"
632
- ? (outputItem["input"] as string)
633
- : undefined;
634
- if (authoritative !== undefined && decodedCallId !== undefined) {
635
- const key = outputItemId ?? decodedCallId;
636
- this.#toolCallArgs.set(key, [authoritative]);
637
- }
571
+
638
572
  if (outputItemId !== undefined) this.#openItems.delete(outputItemId);
639
573
  if (outputIndex !== undefined)
640
574
  this.#openItemsByOutputIndex.delete(outputIndex);
@@ -7,12 +7,33 @@ export interface ToolCallState {
7
7
  }
8
8
 
9
9
  /**
10
- * Per-state argument fragments. `ToolCallState.arguments` stays a plain
11
- * string field for readers, but the tracker accumulates fragments here and
12
- * joins once per read instead of re-copying the whole prefix on every
13
- * delta (which is O(n²) for large tool arguments).
10
+ * Per-state argument fragments and their joined snapshot.
11
+ *
12
+ * The snapshot is cached on the CHUNK ARRAY, not on the state, because the
13
+ * array is deliberately handed forward from a superseded state to its
14
+ * replacement and mutated in place — so a reader holding the older
15
+ * `ToolCallState` still observes the newest total. Caching per state would
16
+ * freeze each state at the value it had when created and silently break that
17
+ * invariant (pinned by test/protocol/tool-identity.test.ts).
18
+ *
19
+ * The cache is dropped on push, so N deltas cost O(N) total: without it each
20
+ * read re-joined the whole array, and a reader that slices the string per
21
+ * delta — as the Messages stream does — re-joined and re-sliced once per
22
+ * fragment, which is O(n²) over a large tool argument.
14
23
  */
15
24
  const argumentChunks = new WeakMap<ToolCallState, string[]>();
25
+ const argumentJoined = new WeakMap<string[], string>();
26
+
27
+ /** The accumulated arguments, joining only when a fragment has been added. */
28
+ function joinedArguments(state: ToolCallState): string {
29
+ const chunks = argumentChunks.get(state);
30
+ if (chunks === undefined) return "";
31
+ const cached = argumentJoined.get(chunks);
32
+ if (cached !== undefined) return cached;
33
+ const value = chunks.join("");
34
+ argumentJoined.set(chunks, value);
35
+ return value;
36
+ }
16
37
 
17
38
  /**
18
39
  * Single tool-call tracking home: id/index/arguments-accumulation keyed by
@@ -60,17 +81,17 @@ export function trackToolCall(
60
81
  tracker.nextIndex = Math.max(tracker.nextIndex, index + 1);
61
82
  const prior = tracker.states.get(tool.id);
62
83
  const name = tool.name ?? prior?.name;
63
- // Append the fragment in O(1) amortized; the joined snapshot is produced
64
- // lazily by the `arguments` getter below, so N deltas cost O(N) total
65
- // instead of O(N²) prefix re-copies. The chunk array is shared forward
66
- // along one call id (superseded states are never read after replacement).
84
+ // Append the fragment in O(1) amortized. The chunk array is shared forward
85
+ // along one call id, so the joined cache is keyed on the array and dropped
86
+ // here: the next read re-joins once and re-caches.
67
87
  const chunks =
68
88
  prior !== undefined ? (argumentChunks.get(prior) ?? [prior.arguments]) : [];
69
89
  chunks.push(tool.args ?? "");
90
+ argumentJoined.delete(chunks);
70
91
  const state: ToolCallState = {
71
92
  id: tool.id || prior?.id || "",
72
93
  get arguments() {
73
- return chunks.join("");
94
+ return joinedArguments(state);
74
95
  },
75
96
  index,
76
97
  };
@@ -80,6 +101,19 @@ export function trackToolCall(
80
101
  return { index, isFirst: prior === undefined, state };
81
102
  }
82
103
 
104
+ /**
105
+ * The not-yet-emitted tail of a call's arguments, from `offset`.
106
+ *
107
+ * Reads the cached joined string rather than re-joining the chunk array, so a
108
+ * caller that asks once per delta — as the Messages stream does — stays
109
+ * linear in the total argument length instead of quadratic. The `slice` itself
110
+ * is unavoidable: the caller needs the tail, and its own length is the output.
111
+ */
112
+ export function argumentsFrom(state: ToolCallState, offset: number): string {
113
+ const all = joinedArguments(state);
114
+ return offset <= 0 ? all : all.slice(offset);
115
+ }
116
+
83
117
  // ===== Emission ledger =====
84
118
 
85
119
  /**