cartethyia 2.5.3 → 2.5.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 (456) hide show
  1. package/.env.example +13 -4
  2. package/README.md +2 -2
  3. package/dist/dashboard/assets/{BannedPage-hfCzi-Ux.js → BannedPage-CADDq4IU.js} +1 -1
  4. package/dist/dashboard/assets/BannedPage-CADDq4IU.js.gz +0 -0
  5. package/dist/dashboard/assets/CliToolDetailPage-_z1hID7E.js +1 -0
  6. package/dist/dashboard/assets/CliToolDetailPage-_z1hID7E.js.gz +0 -0
  7. package/dist/dashboard/assets/{CliToolsPage-DEDyKBlC.js → CliToolsPage-DuO1Z7dq.js} +1 -1
  8. package/dist/dashboard/assets/CliToolsPage-DuO1Z7dq.js.gz +0 -0
  9. package/dist/dashboard/assets/CombosPage-DNEPi3xU.js +1 -0
  10. package/dist/dashboard/assets/CombosPage-DNEPi3xU.js.gz +0 -0
  11. package/dist/dashboard/assets/{ConsoleLogPage-bsLuZmYH.js → ConsoleLogPage-BVdpfiz1.js} +1 -1
  12. package/dist/dashboard/assets/ConsoleLogPage-BVdpfiz1.js.gz +0 -0
  13. package/dist/dashboard/assets/{CustomizationPage-_UWhwziJ.js → CustomizationPage-BR5HHvbi.js} +1 -1
  14. package/dist/dashboard/assets/CustomizationPage-BR5HHvbi.js.gz +0 -0
  15. package/dist/dashboard/assets/HealthEventsModal-mUfcknqm.js +1 -0
  16. package/dist/dashboard/assets/HealthEventsModal-mUfcknqm.js.gz +0 -0
  17. package/dist/dashboard/assets/{LoginPage-DDVIfZGW.js → LoginPage-V5faV9V2.js} +1 -1
  18. package/dist/dashboard/assets/LoginPage-V5faV9V2.js.gz +0 -0
  19. package/dist/dashboard/assets/ModelPicker-C7pDb41R.js +1 -0
  20. package/dist/dashboard/assets/ModelPicker-C7pDb41R.js.gz +0 -0
  21. package/dist/dashboard/assets/OverviewPage-BDu20Jdx.js +1 -0
  22. package/dist/dashboard/assets/OverviewPage-BDu20Jdx.js.gz +0 -0
  23. package/dist/dashboard/assets/ProviderDetailPage-Dllr_aFO.js +3 -0
  24. package/dist/dashboard/assets/ProviderDetailPage-Dllr_aFO.js.gz +0 -0
  25. package/dist/dashboard/assets/ProvidersPage-uPiGVTTK.js +1 -0
  26. package/dist/dashboard/assets/ProvidersPage-uPiGVTTK.js.gz +0 -0
  27. package/dist/dashboard/assets/ProxyPage-BaUkO-4O.js +7 -0
  28. package/dist/dashboard/assets/ProxyPage-BaUkO-4O.js.gz +0 -0
  29. package/dist/dashboard/assets/QuotaPage-o0IFAsFb.js +1 -0
  30. package/dist/dashboard/assets/QuotaPage-o0IFAsFb.js.gz +0 -0
  31. package/dist/dashboard/assets/SettingsPage-CYWqbXjp.js +1 -0
  32. package/dist/dashboard/assets/SettingsPage-CYWqbXjp.js.gz +0 -0
  33. package/dist/dashboard/assets/{SetupPage-D6pmQ86L.js → SetupPage-UB690SE2.js} +1 -1
  34. package/dist/dashboard/assets/SetupPage-UB690SE2.js.gz +0 -0
  35. package/dist/dashboard/assets/{SortableList-DWvKiuSl.js → SortableList-BLujdX7N.js} +1 -1
  36. package/dist/dashboard/assets/SortableList-BLujdX7N.js.gz +0 -0
  37. package/dist/dashboard/assets/{StudioPage-CmkUrgdj.js → StudioPage-D5C2raAc.js} +3 -3
  38. package/dist/dashboard/assets/StudioPage-D5C2raAc.js.gz +0 -0
  39. package/dist/dashboard/assets/UsagePage-8uIcxa1W.js +56 -0
  40. package/dist/dashboard/assets/UsagePage-8uIcxa1W.js.gz +0 -0
  41. package/dist/dashboard/assets/api-keys-DQYLQxpW.js +1 -0
  42. package/dist/dashboard/assets/api-keys-DQYLQxpW.js.gz +0 -0
  43. package/dist/dashboard/assets/app-CX_Z8cMs.css +2 -0
  44. package/dist/dashboard/assets/app-CX_Z8cMs.css.gz +0 -0
  45. package/dist/dashboard/assets/{assert-NHwYq95t.js → assert-C9FD_JwJ.js} +1 -1
  46. package/dist/dashboard/assets/assert-C9FD_JwJ.js.gz +0 -0
  47. package/dist/dashboard/assets/{badge-6uzDDvqs.js → badge-CR72dasT.js} +1 -1
  48. package/dist/dashboard/assets/badge-CR72dasT.js.gz +0 -0
  49. package/dist/dashboard/assets/{banner-4_WNg2WZ.js → banner-CI9_eSfO.js} +1 -1
  50. package/dist/dashboard/assets/banner-CI9_eSfO.js.gz +0 -0
  51. package/dist/dashboard/assets/{card--AtFedAq.js → card-DS7CQEsO.js} +1 -1
  52. package/dist/dashboard/assets/card-DS7CQEsO.js.gz +0 -0
  53. package/dist/dashboard/assets/{cli-tools-B3e7dx3N.js → cli-tools-CNqzrfdb.js} +1 -1
  54. package/dist/dashboard/assets/cli-tools-CNqzrfdb.js.gz +0 -0
  55. package/dist/dashboard/assets/{clipboard-button-NreUx6c8.js → clipboard-button-611pSsaX.js} +1 -1
  56. package/dist/dashboard/assets/clipboard-button-611pSsaX.js.gz +0 -0
  57. package/dist/dashboard/assets/{fingerprint-pattern-BWuHtbW4.js → concurrency-BDF0ok7y.js} +1 -1
  58. package/dist/dashboard/assets/concurrency-BDF0ok7y.js.gz +0 -0
  59. package/dist/dashboard/assets/database-Dz9N3zGd.js +1 -0
  60. package/dist/dashboard/assets/database-Dz9N3zGd.js.gz +0 -0
  61. package/dist/dashboard/assets/dialog-k37exjNt.js +1 -0
  62. package/dist/dashboard/assets/dialog-k37exjNt.js.gz +0 -0
  63. package/dist/dashboard/assets/drawer-CX7N8ucR.js +1 -0
  64. package/dist/dashboard/assets/drawer-CX7N8ucR.js.gz +0 -0
  65. package/dist/dashboard/assets/entry-B9Xvtnfj.js +1 -0
  66. package/dist/dashboard/assets/entry-B9Xvtnfj.js.gz +0 -0
  67. package/dist/dashboard/assets/entry-CTP8AyiS.js +1 -0
  68. package/dist/dashboard/assets/entry-CTP8AyiS.js.gz +0 -0
  69. package/dist/dashboard/assets/entry-DxBHnauy.js +4 -0
  70. package/dist/dashboard/assets/entry-DxBHnauy.js.gz +0 -0
  71. package/dist/dashboard/assets/{github-badge-D_eM3IgG.js → github-badge-sltk1qUi.js} +1 -1
  72. package/dist/dashboard/assets/github-badge-sltk1qUi.js.gz +0 -0
  73. package/dist/dashboard/assets/{index-BydyKaDz.js → index-CmxZr14F.js} +2 -2
  74. package/dist/dashboard/assets/index-CmxZr14F.js.gz +0 -0
  75. package/dist/dashboard/assets/{inline-BcNGF8Gr.js → inline-BhY2sSnO.js} +1 -1
  76. package/dist/dashboard/assets/inline-BhY2sSnO.js.gz +0 -0
  77. package/dist/dashboard/assets/{input-BG41et9G.js → input-V1umFiBk.js} +1 -1
  78. package/dist/dashboard/assets/input-V1umFiBk.js.gz +0 -0
  79. package/dist/dashboard/assets/layout-BBOYycyS.js +1 -0
  80. package/dist/dashboard/assets/layout-BBOYycyS.js.gz +0 -0
  81. package/dist/dashboard/assets/{list-row-Gh_W4OR_.js → list-row-1zzZYM0B.js} +1 -1
  82. package/dist/dashboard/assets/list-row-1zzZYM0B.js.gz +0 -0
  83. package/dist/dashboard/assets/{live-BJGYRBnR.js → live-BivGfuUs.js} +1 -1
  84. package/dist/dashboard/assets/live-BivGfuUs.js.gz +0 -0
  85. package/dist/dashboard/assets/model-bans-CmffjLuG.js +1 -0
  86. package/dist/dashboard/assets/model-bans-CmffjLuG.js.gz +0 -0
  87. package/dist/dashboard/assets/{mutation-BkoLM5Ba.js → mutation-46P0oWMe.js} +1 -1
  88. package/dist/dashboard/assets/mutation-46P0oWMe.js.gz +0 -0
  89. package/dist/dashboard/assets/numbers-DkfMR5Qs.js +2 -0
  90. package/dist/dashboard/assets/numbers-DkfMR5Qs.js.gz +0 -0
  91. package/dist/dashboard/assets/{query-keys-D7wX2vM5.js → query-keys-BFhGpwR1.js} +1 -1
  92. package/dist/dashboard/assets/query-keys-BFhGpwR1.js.gz +0 -0
  93. package/dist/dashboard/assets/{routing-CcusqXA7.js → routing-CAb4F37R.js} +1 -1
  94. package/dist/dashboard/assets/routing-CAb4F37R.js.gz +0 -0
  95. package/dist/dashboard/assets/{select-BO6h8yNv.js → select-BYR_5K2c.js} +2 -2
  96. package/dist/dashboard/assets/select-BYR_5K2c.js.gz +0 -0
  97. package/dist/dashboard/assets/{settings-mpmXTgUa.js → settings-DNX9sydi.js} +1 -1
  98. package/dist/dashboard/assets/settings-DNX9sydi.js.gz +0 -0
  99. package/dist/dashboard/assets/{stack-DwLmjUFK.js → stack-BHTfXdz_.js} +1 -1
  100. package/dist/dashboard/assets/stack-BHTfXdz_.js.gz +0 -0
  101. package/dist/dashboard/assets/{state-DYdQmdAl.js → state-mPDRlQkl.js} +1 -1
  102. package/dist/dashboard/assets/state-mPDRlQkl.js.gz +0 -0
  103. package/dist/dashboard/assets/switch-DOmJxg7H.js +1 -0
  104. package/dist/dashboard/assets/switch-DOmJxg7H.js.gz +0 -0
  105. package/dist/dashboard/assets/terminal-0_IjPWTk.js +1 -0
  106. package/dist/dashboard/assets/terminal-0_IjPWTk.js.gz +0 -0
  107. package/dist/dashboard/assets/tokens-COzario9.js +1 -0
  108. package/dist/dashboard/assets/tokens-COzario9.js.gz +0 -0
  109. package/dist/dashboard/assets/use-modal-focus-By7oJoGd.js +1 -0
  110. package/dist/dashboard/assets/use-modal-focus-By7oJoGd.js.gz +0 -0
  111. package/dist/dashboard/assets/{useMutation-w4u4CVue.js → useMutation-Dem0lXQj.js} +1 -1
  112. package/dist/dashboard/assets/useMutation-Dem0lXQj.js.gz +0 -0
  113. package/dist/dashboard/assets/zap-C4EeVW4q.js +1 -0
  114. package/dist/dashboard/assets/zap-C4EeVW4q.js.gz +0 -0
  115. package/dist/dashboard/donation/qris-risun-marke.png +0 -0
  116. package/dist/dashboard/index.html +1 -1
  117. package/dist/dashboard/index.html.gz +0 -0
  118. package/dist/dashboard/precompressed-assets.json +57 -57
  119. package/migrations/README.md +52 -52
  120. package/migrations/lite-sqlite3/0000_baseline.sql +2 -2
  121. package/package.json +96 -95
  122. package/scripts/cli/admin.ts +125 -0
  123. package/scripts/cli/autostart.ts +237 -0
  124. package/scripts/cli/{ui/background.ts → background.ts} +1 -1
  125. package/scripts/cli/{command/console-menu.ts → console-menu.ts} +275 -248
  126. package/scripts/cli/doctor.ts +37 -0
  127. package/scripts/cli/{support/env.ts → env.ts} +99 -99
  128. package/scripts/cli/{gateway/spawn.ts → gateway.ts} +112 -24
  129. package/scripts/cli/instance.ts +224 -0
  130. package/scripts/cli/main.ts +88 -18
  131. package/scripts/cli/{support/platform.ts → platform.ts} +64 -64
  132. package/scripts/cli/port.ts +100 -0
  133. package/scripts/cli/{support/services.ts → services.ts} +88 -88
  134. package/scripts/cli/{command/setup.ts → setup.ts} +35 -34
  135. package/scripts/cli/{command/start.ts → start.ts} +10 -5
  136. package/scripts/cli/{support/startup-signal.ts → startup-signal.ts} +23 -23
  137. package/scripts/cli/tray/index.ts +62 -12
  138. package/scripts/cli/{command/tray.ts → tray.ts} +208 -208
  139. package/scripts/cli/update.ts +40 -0
  140. package/scripts/cli/version.ts +67 -0
  141. package/scripts/cli/{ui/wordmark.ts → wordmark.ts} +58 -58
  142. package/src/boot/app.ts +11 -7
  143. package/src/boot/dependencies.ts +50 -19
  144. package/src/boot/timeout.ts +2 -13
  145. package/src/config/cooldowns.ts +186 -138
  146. package/src/config/network.ts +8 -14
  147. package/src/config/providers.ts +7 -10
  148. package/src/config/runtime.ts +76 -76
  149. package/src/config/security.ts +15 -0
  150. package/src/config/spec.ts +52 -7
  151. package/src/config/telemetry.ts +26 -0
  152. package/src/config/timeouts.ts +0 -6
  153. package/src/console/admin/model-abuse/contracts.ts +28 -6
  154. package/src/console/admin/model-abuse/routes.ts +56 -3
  155. package/src/console/api-keys/bucket-spend.ts +15 -4
  156. package/src/console/api-keys/contracts.ts +410 -414
  157. package/src/console/api-keys/routes.ts +881 -880
  158. package/src/console/auth/credentials.ts +3 -3
  159. package/src/console/auth/setup-service.ts +2 -2
  160. package/src/console/backup/contracts.ts +15 -2
  161. package/src/console/backup/export-endpoint.ts +6 -2
  162. package/src/console/backup/nine-router.ts +2 -5
  163. package/src/console/backup/service.ts +4 -0
  164. package/src/console/backup/store.ts +192 -29
  165. package/src/console/backup/validate.ts +3 -45
  166. package/src/console/cli-tools/fs-ops.ts +487 -487
  167. package/src/console/console-router-guards.ts +1 -1
  168. package/src/console/console-router.ts +8 -0
  169. package/src/console/data-management/contracts.ts +100 -0
  170. package/src/console/data-management/operations.ts +234 -0
  171. package/src/console/data-management/routes.ts +103 -0
  172. package/src/console/domain-registration.ts +45 -1
  173. package/src/console/observability/contracts.ts +524 -485
  174. package/src/console/observability/live.ts +22 -0
  175. package/src/console/observability/store.ts +1138 -1018
  176. package/src/console/observability/usage-dimensions.ts +4 -4
  177. package/src/console/providers/catalog/catalog-projections.ts +6 -7
  178. package/src/console/providers/catalog/contracts.ts +6 -0
  179. package/src/console/providers/catalog/model-operations.ts +5 -2
  180. package/src/console/providers/catalog/provider-operations.ts +24 -4
  181. package/src/console/providers/catalog/public-model-store.ts +8 -11
  182. package/src/console/providers/catalog/routes.ts +7 -0
  183. package/src/console/providers/catalog/store.ts +1553 -1532
  184. package/src/console/providers/oauth/routes.ts +8 -12
  185. package/src/console/quota/account-quota-shared.ts +52 -39
  186. package/src/console/quota/account-quota-tenant.ts +6 -6
  187. package/src/console/quota/quota-view.ts +1 -1
  188. package/src/console/quota/refresh.ts +441 -429
  189. package/src/console/remote-support/contracts.ts +61 -0
  190. package/src/console/remote-support/routes.ts +65 -0
  191. package/src/console/remote-support/service.ts +207 -0
  192. package/src/console/response-compression.ts +0 -3
  193. package/src/console/routing/pools/import-parse.ts +206 -0
  194. package/src/console/routing/pools/operations.ts +543 -501
  195. package/src/console/routing/pools/probe-result.ts +3 -2
  196. package/src/console/routing/pools/probe-verdict.ts +132 -132
  197. package/src/console/routing/pools/routes.ts +181 -164
  198. package/src/console/routing/pools/store.ts +8 -8
  199. package/src/console/settings/contracts.ts +95 -2
  200. package/src/console/settings/store.ts +81 -3
  201. package/src/console/shared/cursor.ts +8 -8
  202. package/src/console/shared/errors.ts +1 -3
  203. package/src/console/updates/routes.ts +16 -0
  204. package/src/main.ts +10 -0
  205. package/src/network/http2-bidi.ts +2 -4
  206. package/src/network/pool/agent.ts +478 -478
  207. package/src/network/pool/selector.ts +5 -4
  208. package/src/network/retry.ts +1 -2
  209. package/src/network/ssrf.ts +5 -4
  210. package/src/observability/event-compaction.ts +317 -0
  211. package/src/observability/event-day-index.ts +440 -0
  212. package/src/observability/event-log.ts +155 -6
  213. package/src/observability/logger.ts +6 -6
  214. package/src/observability/payload-capture.ts +158 -33
  215. package/src/observability/payload-store.ts +82 -61
  216. package/src/observability/redaction.ts +5 -6
  217. package/src/observability/request-notify.ts +43 -0
  218. package/src/observability/telemetry-status.ts +8 -9
  219. package/src/observability/token-speed.ts +5 -4
  220. package/src/persistence/db-mode.ts +7 -7
  221. package/src/persistence/health-event-flush.ts +36 -0
  222. package/src/persistence/readiness.ts +1 -1
  223. package/src/persistence/schema.ts +75 -29
  224. package/src/persistence/shared-cache.ts +1 -2
  225. package/src/persistence/stores/api-key-store.ts +18 -6
  226. package/src/persistence/stores/share-store.ts +4 -3
  227. package/src/persistence/stores/telemetry-store.ts +33 -11
  228. package/src/protocol/canonical-model.ts +11 -3
  229. package/src/protocol/codecs.ts +139 -6
  230. package/src/protocol/gateway-error.ts +6 -4
  231. package/src/protocol/image-clamp.ts +19 -1
  232. package/src/protocol/registry.ts +8 -0
  233. package/src/protocol/request/chat.ts +21 -1
  234. package/src/protocol/request/codex.ts +10 -1
  235. package/src/protocol/request/gemini.ts +9 -2
  236. package/src/protocol/request/messages.ts +18 -7
  237. package/src/protocol/request/responses.ts +9 -1
  238. package/src/protocol/response/chat.ts +34 -4
  239. package/src/protocol/response/codex.ts +7 -4
  240. package/src/protocol/response/dsml.ts +140 -28
  241. package/src/protocol/response/responses.ts +11 -5
  242. package/src/protocol/stream-error-frames.ts +4 -5
  243. package/src/providers/adapters/compatible-adapter.ts +3 -0
  244. package/src/providers/authentication/jwt-validator.ts +9 -10
  245. package/src/providers/authentication/oauth-refresh-service.ts +9 -11
  246. package/src/providers/bundled-discovery-providers.ts +122 -119
  247. package/src/providers/configured-openai-providers.ts +4 -4
  248. package/src/providers/default-registry.ts +5 -4
  249. package/src/providers/discovery/catalog/catalog-blob.ts +18 -18
  250. package/src/providers/discovery/catalog/catalog-format.ts +3 -6
  251. package/src/providers/discovery/models-dev-catalog.ts +354 -354
  252. package/src/providers/discovery/openai-model-discovery.ts +5 -0
  253. package/src/providers/discovery/openrouter-free-roster.ts +15 -17
  254. package/src/providers/discovery/probe-phases.ts +4 -4
  255. package/src/providers/discovery/probe-wire.ts +6 -13
  256. package/src/providers/discovery/probing-service.ts +1117 -1045
  257. package/src/providers/integrations/alibabacp/alibabacp.ts +1 -1
  258. package/src/providers/integrations/antigravity/antigravity-quota.ts +5 -7
  259. package/src/providers/integrations/buddy/buddy-chat-shared.ts +23 -2
  260. package/src/providers/integrations/buddy/buddy-checkin.ts +1 -4
  261. package/src/providers/integrations/buddy/buddy-cli-headers.ts +23 -6
  262. package/src/providers/integrations/buddy/buddy-conversation-headers.ts +0 -1
  263. package/src/providers/integrations/buddy/buddy-identity.ts +52 -0
  264. package/src/providers/integrations/buddy/buddy-media.ts +2 -2
  265. package/src/providers/integrations/buddy/buddy-usage-store.ts +3 -4
  266. package/src/providers/integrations/buddy/codebuddy/quota.ts +2 -13
  267. package/src/providers/integrations/buddy/codebuddy/shared.ts +118 -115
  268. package/src/providers/integrations/buddy/workbuddy/quota.ts +0 -11
  269. package/src/providers/integrations/buddy/workbuddy/shared.ts +5 -2
  270. package/src/providers/integrations/claude/claude.ts +608 -608
  271. package/src/providers/integrations/cline/cline-quota.ts +27 -20
  272. package/src/providers/integrations/cline/cline.ts +537 -539
  273. package/src/providers/integrations/codex/codex-headers.ts +25 -39
  274. package/src/providers/integrations/codex/codex-oauth.ts +1 -1
  275. package/src/providers/integrations/codex/codex.ts +2 -29
  276. package/src/providers/integrations/commandcode/commandcode.ts +674 -675
  277. package/src/providers/integrations/commandcode/discovery.ts +117 -116
  278. package/src/providers/integrations/cursor/cursor-auth.ts +418 -418
  279. package/src/providers/integrations/cursor/cursor-quota.ts +309 -0
  280. package/src/providers/integrations/cursor/cursor-request.ts +616 -471
  281. package/src/providers/integrations/cursor/cursor-session-manager.ts +319 -281
  282. package/src/providers/integrations/cursor/cursor.ts +1333 -960
  283. package/src/providers/integrations/cursor/protocol/messages.ts +25 -0
  284. package/src/providers/integrations/factory/factory-models.ts +3 -5
  285. package/src/providers/integrations/factory/factory.ts +8 -1
  286. package/src/providers/integrations/gemini.ts +6 -1
  287. package/src/providers/integrations/grok/grok-install-id.ts +2 -3
  288. package/src/providers/integrations/grok/grok-quota.ts +279 -177
  289. package/src/providers/integrations/grok/grok-ratelimit-store.ts +73 -0
  290. package/src/providers/integrations/grok/grok-ratelimit.ts +218 -0
  291. package/src/providers/integrations/grok/grok.ts +6 -7
  292. package/src/providers/integrations/hermes.ts +14 -18
  293. package/src/providers/integrations/kiro/kiro-discovery.ts +1 -1
  294. package/src/providers/integrations/kiro/kiro.ts +1 -1
  295. package/src/providers/integrations/mistral.ts +6 -6
  296. package/src/providers/integrations/opencode/fingerprint.ts +3 -3
  297. package/src/providers/integrations/opencode/provider.ts +19 -20
  298. package/src/providers/integrations/qoder/qoder-catalog.ts +450 -450
  299. package/src/providers/integrations/qoder/qoder-oauth.ts +388 -310
  300. package/src/providers/integrations/qoder/qoder-quota.ts +192 -0
  301. package/src/providers/integrations/qoder/qoder.ts +884 -865
  302. package/src/providers/integrations/shared/connect.ts +6 -6
  303. package/src/providers/integrations/trae/trae-catalog.ts +1 -2
  304. package/src/providers/integrations/trae/trae-quota.ts +384 -0
  305. package/src/providers/integrations/trae/trae.ts +2 -2
  306. package/src/providers/integrations/zcode/oauth.ts +6 -1
  307. package/src/providers/operations/account-health-service.ts +75 -27
  308. package/src/providers/operations/account-reset-service.ts +1 -1
  309. package/src/providers/operations/client-versions.ts +6 -6
  310. package/src/providers/operations/custom-cli-headers.ts +3 -4
  311. package/src/providers/operations/provider-catalog-seeder.ts +95 -95
  312. package/src/providers/operations/provider-catalog-service.ts +3 -4
  313. package/src/providers/provider-contracts.ts +7 -0
  314. package/src/providers/provider-metadata.ts +10 -8
  315. package/src/providers/provider-registry.ts +6 -7
  316. package/src/providers/provider-usage.ts +1 -1
  317. package/src/providers/quota/quota-support.ts +8 -0
  318. package/src/security/abuse.ts +15 -10
  319. package/src/security/access-control.ts +144 -146
  320. package/src/security/admission/contracts.ts +11 -0
  321. package/src/security/admission/in-memory-store.ts +4 -3
  322. package/src/security/admission/lease-sweep.ts +6 -6
  323. package/src/security/admission/reasons.ts +5 -1
  324. package/src/security/admission/redis-store.ts +211 -210
  325. package/src/security/admission/service.ts +4 -0
  326. package/src/security/api-key-auth.ts +27 -17
  327. package/src/security/ip-boundary.ts +155 -15
  328. package/src/security/model-abuse.ts +366 -94
  329. package/src/security/outbound-headers.ts +3 -4
  330. package/src/shared/sleep.ts +4 -0
  331. package/src/transport/dispatch/attempt-finalize.ts +47 -5
  332. package/src/transport/dispatch/attempt-loop.ts +531 -530
  333. package/src/transport/dispatch/generation.ts +6 -0
  334. package/src/transport/dispatch/proxy-request.ts +24 -1
  335. package/src/transport/dispatch/ratelimit-capture.ts +37 -0
  336. package/src/transport/dispatch/stream-bridge.ts +1 -3
  337. package/src/transport/dispatch/streaming/finalize.ts +1 -1
  338. package/src/transport/dispatch/streaming/stream.ts +19 -6
  339. package/src/transport/dispatch/upstream.ts +9 -0
  340. package/src/transport/failure-policy.ts +162 -8
  341. package/src/transport/guards/auth-guard.ts +7 -4
  342. package/src/transport/guards/concurrency-guard.ts +0 -5
  343. package/src/transport/guards/error-lifecycle.ts +13 -6
  344. package/src/transport/guards/pipeline.ts +15 -3
  345. package/src/transport/guards/request-context.ts +8 -2
  346. package/src/transport/request/inflight.ts +5 -6
  347. package/src/transport/request/preparer.ts +9 -8
  348. package/src/transport/request/state.ts +21 -8
  349. package/src/transport/request/tenant-preferences.ts +60 -0
  350. package/src/transport/routing/admission.ts +4 -0
  351. package/src/transport/routing/route-catalog.ts +65 -10
  352. package/src/transport/routing/route-model.ts +46 -14
  353. package/src/transport/routing/router.ts +21 -14
  354. package/src/transport/surface/completion.ts +2 -3
  355. package/src/transport/surface/dialects.ts +5 -6
  356. package/src/transport/surface/responses/encode.ts +38 -0
  357. package/src/transport/surface/responses/parse.ts +4 -0
  358. package/src/transport/translation/capabilities.ts +2 -3
  359. package/src/transport/translation/quirks.ts +3 -3
  360. package/src/workers/buddy-growth-tasks.ts +5 -10
  361. package/src/workers/daily-checkin.ts +17 -23
  362. package/src/workers/quota-refresh-sweep.ts +26 -26
  363. package/dist/dashboard/assets/BannedPage-hfCzi-Ux.js.gz +0 -0
  364. package/dist/dashboard/assets/CliToolDetailPage-DLGqYEwI.js +0 -1
  365. package/dist/dashboard/assets/CliToolDetailPage-DLGqYEwI.js.gz +0 -0
  366. package/dist/dashboard/assets/CliToolsPage-DEDyKBlC.js.gz +0 -0
  367. package/dist/dashboard/assets/CombosPage-CfuYiQ8Z.js +0 -1
  368. package/dist/dashboard/assets/CombosPage-CfuYiQ8Z.js.gz +0 -0
  369. package/dist/dashboard/assets/ConsoleLogPage-bsLuZmYH.js.gz +0 -0
  370. package/dist/dashboard/assets/CustomizationPage-_UWhwziJ.js.gz +0 -0
  371. package/dist/dashboard/assets/HealthEventsModal-Cjfc5WFS.js +0 -1
  372. package/dist/dashboard/assets/HealthEventsModal-Cjfc5WFS.js.gz +0 -0
  373. package/dist/dashboard/assets/LoginPage-DDVIfZGW.js.gz +0 -0
  374. package/dist/dashboard/assets/ModelPicker-CWkTUtpX.js +0 -1
  375. package/dist/dashboard/assets/ModelPicker-CWkTUtpX.js.gz +0 -0
  376. package/dist/dashboard/assets/OverviewPage-B53YYMng.js +0 -1
  377. package/dist/dashboard/assets/OverviewPage-B53YYMng.js.gz +0 -0
  378. package/dist/dashboard/assets/ProviderDetailPage-B0KaAQf0.js +0 -3
  379. package/dist/dashboard/assets/ProviderDetailPage-B0KaAQf0.js.gz +0 -0
  380. package/dist/dashboard/assets/ProvidersPage-Dx31DgIO.js +0 -1
  381. package/dist/dashboard/assets/ProvidersPage-Dx31DgIO.js.gz +0 -0
  382. package/dist/dashboard/assets/ProxyPage-BpnR4kXT.js +0 -7
  383. package/dist/dashboard/assets/ProxyPage-BpnR4kXT.js.gz +0 -0
  384. package/dist/dashboard/assets/QuotaPage-BpOQCnun.js +0 -1
  385. package/dist/dashboard/assets/QuotaPage-BpOQCnun.js.gz +0 -0
  386. package/dist/dashboard/assets/SettingsPage-DV1eo2J_.js +0 -1
  387. package/dist/dashboard/assets/SettingsPage-DV1eo2J_.js.gz +0 -0
  388. package/dist/dashboard/assets/SetupPage-D6pmQ86L.js.gz +0 -0
  389. package/dist/dashboard/assets/SortableList-DWvKiuSl.js.gz +0 -0
  390. package/dist/dashboard/assets/StudioPage-CmkUrgdj.js.gz +0 -0
  391. package/dist/dashboard/assets/UsagePage-B47uOydr.js +0 -56
  392. package/dist/dashboard/assets/UsagePage-B47uOydr.js.gz +0 -0
  393. package/dist/dashboard/assets/api-keys-uNywYlfv.js +0 -1
  394. package/dist/dashboard/assets/api-keys-uNywYlfv.js.gz +0 -0
  395. package/dist/dashboard/assets/app-DxivpfYG.css +0 -2
  396. package/dist/dashboard/assets/app-DxivpfYG.css.gz +0 -0
  397. package/dist/dashboard/assets/assert-NHwYq95t.js.gz +0 -0
  398. package/dist/dashboard/assets/badge-6uzDDvqs.js.gz +0 -0
  399. package/dist/dashboard/assets/banner-4_WNg2WZ.js.gz +0 -0
  400. package/dist/dashboard/assets/card--AtFedAq.js.gz +0 -0
  401. package/dist/dashboard/assets/cli-tools-B3e7dx3N.js.gz +0 -0
  402. package/dist/dashboard/assets/clipboard-button-NreUx6c8.js.gz +0 -0
  403. package/dist/dashboard/assets/dialog-BtpsJU98.js +0 -1
  404. package/dist/dashboard/assets/dialog-BtpsJU98.js.gz +0 -0
  405. package/dist/dashboard/assets/download-A0SW9OCW.js +0 -1
  406. package/dist/dashboard/assets/download-A0SW9OCW.js.gz +0 -0
  407. package/dist/dashboard/assets/drawer-WwwtzihF.js +0 -1
  408. package/dist/dashboard/assets/drawer-WwwtzihF.js.gz +0 -0
  409. package/dist/dashboard/assets/entry-CkryrIea.js +0 -4
  410. package/dist/dashboard/assets/entry-CkryrIea.js.gz +0 -0
  411. package/dist/dashboard/assets/entry-TJIye_jT.js +0 -1
  412. package/dist/dashboard/assets/entry-TJIye_jT.js.gz +0 -0
  413. package/dist/dashboard/assets/entry-jy9_4-Ke.js +0 -1
  414. package/dist/dashboard/assets/entry-jy9_4-Ke.js.gz +0 -0
  415. package/dist/dashboard/assets/fingerprint-pattern-BWuHtbW4.js.gz +0 -0
  416. package/dist/dashboard/assets/github-badge-D_eM3IgG.js.gz +0 -0
  417. package/dist/dashboard/assets/index-BydyKaDz.js.gz +0 -0
  418. package/dist/dashboard/assets/inline-BcNGF8Gr.js.gz +0 -0
  419. package/dist/dashboard/assets/input-BG41et9G.js.gz +0 -0
  420. package/dist/dashboard/assets/layout-C8mer_Ak.js +0 -1
  421. package/dist/dashboard/assets/layout-C8mer_Ak.js.gz +0 -0
  422. package/dist/dashboard/assets/list-row-Gh_W4OR_.js.gz +0 -0
  423. package/dist/dashboard/assets/live-BJGYRBnR.js.gz +0 -0
  424. package/dist/dashboard/assets/model-bans-DeVLDA7I.js +0 -1
  425. package/dist/dashboard/assets/model-bans-DeVLDA7I.js.gz +0 -0
  426. package/dist/dashboard/assets/mutation-BkoLM5Ba.js.gz +0 -0
  427. package/dist/dashboard/assets/numbers-Bs4LOOuf.js +0 -2
  428. package/dist/dashboard/assets/numbers-Bs4LOOuf.js.gz +0 -0
  429. package/dist/dashboard/assets/query-keys-D7wX2vM5.js.gz +0 -0
  430. package/dist/dashboard/assets/routing-CcusqXA7.js.gz +0 -0
  431. package/dist/dashboard/assets/select-BO6h8yNv.js.gz +0 -0
  432. package/dist/dashboard/assets/settings-mpmXTgUa.js.gz +0 -0
  433. package/dist/dashboard/assets/sparkles-BRaRUxAU.js +0 -1
  434. package/dist/dashboard/assets/sparkles-BRaRUxAU.js.gz +0 -0
  435. package/dist/dashboard/assets/stack-DwLmjUFK.js.gz +0 -0
  436. package/dist/dashboard/assets/state-DYdQmdAl.js.gz +0 -0
  437. package/dist/dashboard/assets/switch-CidSfbG7.js +0 -1
  438. package/dist/dashboard/assets/switch-CidSfbG7.js.gz +0 -0
  439. package/dist/dashboard/assets/terminal-Bd4ee8TA.js +0 -1
  440. package/dist/dashboard/assets/terminal-Bd4ee8TA.js.gz +0 -0
  441. package/dist/dashboard/assets/tokens-DDxMbSRL.js +0 -1
  442. package/dist/dashboard/assets/tokens-DDxMbSRL.js.gz +0 -0
  443. package/dist/dashboard/assets/use-presence-BSZEBx7U.js +0 -1
  444. package/dist/dashboard/assets/use-presence-BSZEBx7U.js.gz +0 -0
  445. package/dist/dashboard/assets/useMutation-w4u4CVue.js.gz +0 -0
  446. package/scripts/cli/command/admin.ts +0 -65
  447. package/scripts/cli/command/doctor.ts +0 -257
  448. package/scripts/cli/command/update.ts +0 -23
  449. package/scripts/cli/gateway/drain-token.ts +0 -78
  450. package/scripts/cli/gateway/instance-lock.ts +0 -105
  451. package/scripts/cli/gateway/stop.ts +0 -51
  452. package/scripts/cli/support/bun.ts +0 -27
  453. package/scripts/cli/support/version.ts +0 -37
  454. /package/dist/dashboard/assets/{app-CafWtVXA.js → app-BIt446yE.js} +0 -0
  455. /package/dist/dashboard/assets/{app-CafWtVXA.js.gz → app-BIt446yE.js.gz} +0 -0
  456. /package/scripts/cli/{ui/prompt.ts → prompt.ts} +0 -0
@@ -332,9 +332,9 @@ export const VERSION_SOURCES = {
332
332
  fallback: "1.3.11",
333
333
  sources: [
334
334
  {
335
- // Kiro's downloads page no longer exposes a stable version string.
336
- // Homebrew tracks the official installer URL and exposes its version
337
- // as JSON; keep the product page parser as a fallback.
335
+ // Kiro's downloads page exposes no stable version string. Homebrew
336
+ // tracks the official installer URL and exposes its version as JSON;
337
+ // keep the product page parser as a fallback.
338
338
  url: "https://formulae.brew.sh/api/cask/kiro.json",
339
339
  extract: kiroCaskVersion,
340
340
  },
@@ -488,8 +488,8 @@ const PROVIDER_VERSION_RESOLVERS: Readonly<Record<string, ClientVersionResolver>
488
488
  "opencode-zen": resolvers.opencode,
489
489
  "opencode-go": resolvers.opencode,
490
490
  // The identity this provider is dispatched under is the CLI's, so the route
491
- // identity reports the CLI version. `clineClient` (the VS Code extension
492
- // manifest) is no longer the client this provider claims to be.
491
+ // identity reports the CLI version, not the VS Code extension manifest
492
+ // (`clineClient`).
493
493
  cline: resolvers.clineCli,
494
494
  codex: resolvers.codex,
495
495
  qoder: resolvers.qoder,
@@ -670,7 +670,7 @@ export function buildCodeBuddyUserAgent(
670
670
  ): string {
671
671
  // 1:1 with codebuddy2api's identity_headers(): the CLI profile uses ONE version
672
672
  // (the npm CLI package pin) for both halves, and the IDE profile uses the other
673
- // product's version. This previously always used `version`, so "CLI" produced
673
+ // product's version. Using `version` for both would make "CLI" produce
674
674
  // `CLI/<ide> CodeBuddy/<ide>` — a CLI claiming the desktop build.
675
675
  const resolved = identity === "CLI" ? cnVersion : version;
676
676
  return `${identity}/${resolved} CodeBuddy/${resolved}`;
@@ -34,10 +34,9 @@ export function buildCustomCodexCliHeaders(): Record<string, string> {
34
34
  *
35
35
  * This is the route-level default for a provider whose operator set no client
36
36
  * identity of their own: a bare API key identifies as the Claude Code CLI
37
- * rather than as nothing. It used to be the Codex CLI identity
38
- * (`codex_cli_rs/…`), which named a different vendor's client on requests that
39
- * most often reached neither — an upstream that keys on the client string
40
- * would act on it.
37
+ * rather than as nothing. The Codex CLI identity (`codex_cli_rs/…`) would name
38
+ * a different vendor's client on requests that most often reach neither — an
39
+ * upstream that keys on the client string would act on it.
41
40
  *
42
41
  * The version comes from the live getter for the same reason
43
42
  * {@link buildCustomClaudeCliHeaders} does: the pinned `claude-fingerprint`
@@ -1,95 +1,95 @@
1
- /** Durable materialization of bundled provider model metadata. */
2
- import { and, eq, sql } from "drizzle-orm";
3
- import type { CartethyiaDatabase } from "../../persistence/db-handle";
4
- import { models } from "../../persistence/schema";
5
- import type { ModelDefinition } from "../provider-model-definition";
6
-
7
- /** Persists compiled provider model metadata and reconciles drifted capability flags. */
8
- export async function seedBundledModels(
9
- db: CartethyiaDatabase,
10
- builtinModels: ReadonlyMap<string, readonly ModelDefinition[]>,
11
- ): Promise<void> {
12
- for (const [providerId, definitions] of builtinModels) {
13
- if (definitions.length === 0) continue;
14
- // Drop builtin rows the current catalog no longer declares, including rows
15
- // whose model id is still present but whose endpoint path drifted (e.g. a
16
- // provider that moved from `/chat/completions` to `/v2/chat/completions`).
17
- // The unique key is (provider, model, endpoint), so a changed endpoint
18
- // would otherwise insert a second row and leave the stale route behind —
19
- // the model list would then show duplicates and probes could hit the dead
20
- // path. Composite `NOT IN` keeps exactly the (model, endpoint) pairs the
21
- // catalog owns.
22
- const pairs = definitions.map(
23
- (definition) => sql`(${definition.modelId}, ${definition.endpointPath})`,
24
- );
25
- await db
26
- .delete(models)
27
- .where(
28
- and(
29
- eq(models.providerId, providerId),
30
- eq(models.source, "builtin"),
31
- sql`(${models.modelId}, ${models.endpointPath}) NOT IN (${sql.join(pairs, sql`, `)})`,
32
- ),
33
- );
34
- }
35
- const now = new Date();
36
- const seen = new Set<string>();
37
- const rows = [...builtinModels.entries()].flatMap(([providerId, definitions]) =>
38
- definitions.flatMap((definition) => {
39
- const key = `${providerId}\u0000${definition.modelId}\u0000${definition.endpointPath}`;
40
- if (seen.has(key)) return [];
41
- seen.add(key);
42
- return [
43
- {
44
- providerId,
45
- modelId: definition.modelId,
46
- wireFamily: definition.wireFamily,
47
- serviceKind: definition.serviceKind ?? "llm",
48
- endpointPath: definition.endpointPath,
49
- contextLimit: definition.contextLimit,
50
- outputLimit: definition.outputLimit,
51
- modalities: definition.modalities,
52
- reasoning: definition.reasoning,
53
- toolCall: definition.toolCall,
54
- cost: definition.cost,
55
- // A static row either knows it is refused on a free plan or says
56
- // nothing. Never coerced to false: the column is three-valued, and
57
- // "nobody established it" is not the same claim as "served on free".
58
- proOnly: definition.proOnly ?? null,
59
- // Same three-valued rule for the inverse: the provider's directory
60
- // states the tier, so the flag is carried rather than derived.
61
- freeTier: definition.freeTier ?? null,
62
- source: "builtin",
63
- sourceUpdatedAt: now,
64
- enabled: true,
65
- },
66
- ];
67
- }),
68
- );
69
- if (rows.length === 0) return;
70
- // Reconcile static metadata on conflict rather than skipping: capability
71
- // flags (toolCall/reasoning/webSearch), wire family, limits, and cost can
72
- // drift on a pre-existing row (e.g. a discovery seeded `tool_call=false`
73
- // before the static catalog was corrected). `source` is forced back to
74
- // `builtin`: a fetch upsert may have flipped a builtin row to `discovered`,
75
- // and the static catalog owns these ids. `enabled` is operator-owned and
76
- // must never be reset here.
77
- await db.insert(models).values(rows).onConflictDoUpdate({
78
- target: [models.providerId, models.modelId, models.endpointPath],
79
- set: {
80
- wireFamily: sql`excluded.wire_family`,
81
- serviceKind: sql`excluded.service_kind`,
82
- endpointPath: sql`excluded.endpoint_path`,
83
- contextLimit: sql`excluded.context_limit`,
84
- outputLimit: sql`excluded.output_limit`,
85
- modalities: sql`excluded.modalities`,
86
- reasoning: sql`excluded.reasoning`,
87
- toolCall: sql`excluded.tool_call`,
88
- cost: sql`excluded.cost`,
89
- proOnly: sql`excluded.pro_only`,
90
- freeTier: sql`excluded.free_tier`,
91
- source: sql`'builtin'`,
92
- sourceUpdatedAt: sql`excluded.source_updated_at`,
93
- },
94
- });
95
- }
1
+ /** Durable materialization of bundled provider model metadata. */
2
+ import { and, eq, sql } from "drizzle-orm";
3
+ import type { CartethyiaDatabase } from "../../persistence/db-handle";
4
+ import { models } from "../../persistence/schema";
5
+ import type { ModelDefinition } from "../provider-model-definition";
6
+
7
+ /** Persists compiled provider model metadata and reconciles drifted capability flags. */
8
+ export async function seedBundledModels(
9
+ db: CartethyiaDatabase,
10
+ builtinModels: ReadonlyMap<string, readonly ModelDefinition[]>,
11
+ ): Promise<void> {
12
+ for (const [providerId, definitions] of builtinModels) {
13
+ if (definitions.length === 0) continue;
14
+ // Drop builtin rows the current catalog no longer declares, including rows
15
+ // whose model id is still present but whose endpoint path drifted (e.g. a
16
+ // provider that moved from `/chat/completions` to `/v2/chat/completions`).
17
+ // The unique key is (provider, model, endpoint), so a changed endpoint
18
+ // would otherwise insert a second row and leave the stale route behind —
19
+ // the model list then shows duplicates and probes can hit the dead path.
20
+ // Composite `NOT IN` keeps exactly the (model, endpoint) pairs the catalog
21
+ // owns.
22
+ const pairs = definitions.map(
23
+ (definition) => sql`(${definition.modelId}, ${definition.endpointPath})`,
24
+ );
25
+ await db
26
+ .delete(models)
27
+ .where(
28
+ and(
29
+ eq(models.providerId, providerId),
30
+ eq(models.source, "builtin"),
31
+ sql`(${models.modelId}, ${models.endpointPath}) NOT IN (${sql.join(pairs, sql`, `)})`,
32
+ ),
33
+ );
34
+ }
35
+ const now = new Date();
36
+ const seen = new Set<string>();
37
+ const rows = [...builtinModels.entries()].flatMap(([providerId, definitions]) =>
38
+ definitions.flatMap((definition) => {
39
+ const key = `${providerId}\u0000${definition.modelId}\u0000${definition.endpointPath}`;
40
+ if (seen.has(key)) return [];
41
+ seen.add(key);
42
+ return [
43
+ {
44
+ providerId,
45
+ modelId: definition.modelId,
46
+ wireFamily: definition.wireFamily,
47
+ serviceKind: definition.serviceKind ?? "llm",
48
+ endpointPath: definition.endpointPath,
49
+ contextLimit: definition.contextLimit,
50
+ outputLimit: definition.outputLimit,
51
+ modalities: definition.modalities,
52
+ reasoning: definition.reasoning,
53
+ toolCall: definition.toolCall,
54
+ cost: definition.cost,
55
+ // A static row either knows it is refused on a free plan or says
56
+ // nothing. Never coerced to false: the column is three-valued, and
57
+ // "nobody established it" is not the same claim as "served on free".
58
+ proOnly: definition.proOnly ?? null,
59
+ // Same three-valued rule for the inverse: the provider's directory
60
+ // states the tier, so the flag is carried rather than derived.
61
+ freeTier: definition.freeTier ?? null,
62
+ source: "builtin",
63
+ sourceUpdatedAt: now,
64
+ enabled: true,
65
+ },
66
+ ];
67
+ }),
68
+ );
69
+ if (rows.length === 0) return;
70
+ // Reconcile static metadata on conflict rather than skipping: capability
71
+ // flags (toolCall/reasoning/webSearch), wire family, limits, and cost can
72
+ // drift on a pre-existing row (e.g. a discovery seeded `tool_call=false`
73
+ // before the static catalog was corrected). `source` is forced back to
74
+ // `builtin`: a fetch upsert may have flipped a builtin row to `discovered`,
75
+ // and the static catalog owns these ids. `enabled` is operator-owned and
76
+ // must never be reset here.
77
+ await db.insert(models).values(rows).onConflictDoUpdate({
78
+ target: [models.providerId, models.modelId, models.endpointPath],
79
+ set: {
80
+ wireFamily: sql`excluded.wire_family`,
81
+ serviceKind: sql`excluded.service_kind`,
82
+ endpointPath: sql`excluded.endpoint_path`,
83
+ contextLimit: sql`excluded.context_limit`,
84
+ outputLimit: sql`excluded.output_limit`,
85
+ modalities: sql`excluded.modalities`,
86
+ reasoning: sql`excluded.reasoning`,
87
+ toolCall: sql`excluded.tool_call`,
88
+ cost: sql`excluded.cost`,
89
+ proOnly: sql`excluded.pro_only`,
90
+ freeTier: sql`excluded.free_tier`,
91
+ source: sql`'builtin'`,
92
+ sourceUpdatedAt: sql`excluded.source_updated_at`,
93
+ },
94
+ });
95
+ }
@@ -134,7 +134,7 @@ export async function seedBundledProviders(db: CartethyiaDatabase): Promise<void
134
134
  * console-created provider carries a tenant — so an unowned id absent from the
135
135
  * bundle is an orphan by construction, and a tenant-owned BYOK row is never in
136
136
  * scope. The model seeder prunes its retired rows the same way; this is the
137
- * provider-level half that was missing.
137
+ * provider-level half.
138
138
  *
139
139
  * Separate from {@link seedBundledProviders} rather than folded into it: the
140
140
  * harness calls that seeder on import to converge a shared database, and a
@@ -226,9 +226,8 @@ export async function bundledModelCatalog(
226
226
 
227
227
  /**
228
228
  * Re-seeds the compiled catalogs, called when a provider gains an account after
229
- * boot. Seeding is no longer account-gated, so this is now simply the same walk
230
- * as at boot; it exists because an account makes the rows routable and the
231
- * snapshot must reflect that without waiting for a restart.
229
+ * boot. It runs the same walk as at boot: an account makes the rows routable and
230
+ * the snapshot must reflect that without waiting for a restart.
232
231
  *
233
232
  * Gated on {@link currentCatalogRevision}: this is the seeder the snapshot
234
233
  * builder calls on every rebuild, and the invalidation that triggers it is a
@@ -146,6 +146,13 @@ export interface ProviderDispatchContext {
146
146
  readonly request_headers?: Readonly<Record<string, string>> | undefined;
147
147
  /** Stable per-conversation cache affinity passed through dispatch. */
148
148
  readonly conversation_affinity?: string | undefined;
149
+ /**
150
+ * Per-tenant ceiling for one inline attachment, in bytes. `undefined` means
151
+ * the deployment default. Read by wire encoders that clamp images, which
152
+ * otherwise have no access to tenant preferences — they are pure translation
153
+ * and hold no database handle.
154
+ */
155
+ readonly image_clamp_max_bytes?: number | undefined;
149
156
  /** Opaque identity shared by retries of one logical gateway request. */
150
157
  readonly request_identity?: object | undefined;
151
158
  /**
@@ -316,6 +316,12 @@ const RAW_BUNDLED_PROVIDER_METADATA = [
316
316
  hasAdapterUserAgent: true,
317
317
  },
318
318
  { id: "bai", displayName: "B.AI", baseUrl: "https://api.b.ai/v1", credentialUrl: "https://b.ai" },
319
+ {
320
+ id: "larprouter",
321
+ displayName: "LarRouter",
322
+ baseUrl: "https://larprouter.com/v1",
323
+ credentialUrl: "https://larprouter.com",
324
+ },
319
325
  { id: "inferhub", displayName: "InferHub", baseUrl: "https://api.inferhub.dev/v1", defaultSkipProxy: true },
320
326
  { id: "aihubmix", displayName: "AiHubMix", baseUrl: "https://aihubmix.com/v1", credentialUrl: "https://aihubmix.com/token" },
321
327
  { id: "tokenharbor", displayName: "TokenHarbor", baseUrl: "https://tokenharbor.ai/v1", credentialUrl: "https://tokenharbor.ai" },
@@ -409,18 +415,14 @@ const RAW_BUNDLED_PROVIDER_METADATA = [
409
415
  /**
410
416
  * Web search is a route of its own, never a model capability.
411
417
  *
412
- * There used to be a manual list of providers "whose hosted search had been
413
- * proven", and a `providerSupportsWebSearch` helper that consulted it. Both are
414
- * gone. A model row no longer advertises search, and a provider serves search
418
+ * A model row does not advertise search, and a provider serves search
415
419
  * purely by declaring a `serviceKind: "websearch"` catalog row — which is what
416
420
  * Exa, Parallel, and Gemini do. Those rows answer `POST /v1/search` and are
417
421
  * never reached from a chat turn.
418
422
  *
419
- * The reason is that the flag was never a capability gate: `deriveRequiredCapabilities`
420
- * deliberately does not derive `web_search`, so no chat request was ever
421
- * filtered or degraded by it. Its only real effect was to smuggle `llm` rows
422
- * (Codex, Antigravity) into the fallback candidate list as if they were search
423
- * routes, and to show a badge in the console. The fallback is gone too: a chat
423
+ * A search flag is not a capability gate: `deriveRequiredCapabilities`
424
+ * deliberately does not derive `web_search`, so no chat request is
425
+ * filtered or degraded by it. A chat
424
426
  * turn is dispatched once to the route its model selects, and a hosted search
425
427
  * declaration is answered by that upstream, not executed by the gateway.
426
428
  */
@@ -38,8 +38,8 @@ export interface ProviderModule {
38
38
  * `upstream_host` is the SSRF-validated network-binding target for this
39
39
  * provider — carried on the same registration object as the adapter loader
40
40
  * so adding a provider touches one call-site instead of a second map kept
41
- * in sync by hand elsewhere (`transport/dispatch/proxy-request.ts` used to hardcode this
42
- * separately; `providers/operations/provider-catalog-service.ts` now derives it from here).
41
+ * in sync by hand elsewhere; `providers/operations/provider-catalog-service.ts`
42
+ * derives it from here.
43
43
  */
44
44
  export interface ProviderRegistration {
45
45
  readonly provider_id: ProviderId;
@@ -240,11 +240,10 @@ export class ProviderRegistry {
240
240
  * check — no module is loaded — for list projections that must hide the
241
241
  * "Fetch models" control from providers with no discovery endpoint.
242
242
  *
243
- * This is the question the projection should be asking. "Is this a builtin?"
244
- * was a proxy for it that was true for every provider when the catalog had no
245
- * discovery-backed builtins, and wrong for every one added since: a builtin
246
- * that registers a resolver cannot list any model until an operator runs a
247
- * sync, so hiding the control leaves it permanently empty.
243
+ * This is the question the projection should be asking; "is this a builtin?"
244
+ * is not a proxy for it. A builtin that registers a resolver cannot list any
245
+ * model until an operator runs a sync, so hiding the control on the strength
246
+ * of builtin status alone leaves it permanently empty.
248
247
  */
249
248
  hasModelDiscovery(providerId: string): boolean {
250
249
  return this.#registrations.get(parseProviderId(providerId))?.loadModelDiscovery !== undefined;
@@ -195,7 +195,7 @@ export function normalizeUsage(input: Record<string, unknown>): UsageRecord {
195
195
  // the zeroed `*_tokens_details.cached_tokens` those bridges also emit.
196
196
  // Flat `cached_tokens` is the intermediate shape `mapGeminiUsage`
197
197
  // (Gemini/Antigravity) and CommandCode emit; `cachedContentTokenCount` is
198
- // Gemini's raw `usageMetadata` field. Both were previously dropped.
198
+ // Gemini's raw `usageMetadata` field. Both are consumed here.
199
199
  if (
200
200
  cached_input_tokens === "unavailable" &&
201
201
  typeof input.cached_tokens === "number"
@@ -54,6 +54,14 @@ export interface QuotaCollectionContext {
54
54
  readonly auth_state?: Readonly<Record<string, unknown>> | undefined;
55
55
  /** Stored credential kind, for surfaces that differ per auth family. */
56
56
  readonly credential_kind?: "api_key" | "oauth" | "none" | undefined;
57
+ /**
58
+ * The account the reading belongs to, when the caller knows it.
59
+ *
60
+ * Needed by collectors that can reuse a value captured from real traffic:
61
+ * that reading is keyed by account, so without this the collector cannot
62
+ * find it and spends a request rediscovering what dispatch already learned.
63
+ */
64
+ readonly accountId?: string | undefined;
57
65
  }
58
66
 
59
67
  /** Provider quota callback supplied by the canonical provider registry. */
@@ -78,10 +78,10 @@ export interface IpAbuseOutcome {
78
78
  *
79
79
  * The store is the only component that knows this: a ban's remainder is its
80
80
  * marker's TTL, and a rate-limited window's remainder is the age of its
81
- * oldest attempt. Both are server state. The middleware used to answer every
82
- * 429 with a fixed `retry-after: 3600`, which told a client hitting a 60-second
83
- * window to wait sixty times longer than it had to — and told a client whose
84
- * ban had nearly elapsed to wait the full hour again.
81
+ * oldest attempt. Both are server state. Answering every 429 with a fixed
82
+ * `retry-after: 3600` would tell a client hitting a 60-second window to wait
83
+ * sixty times longer than it had to — and tell a client whose ban had nearly
84
+ * elapsed to wait the full hour again.
85
85
  */
86
86
  readonly retryAfterMs: number;
87
87
  }
@@ -224,7 +224,7 @@ export class InMemoryIpAbuseStore implements IpAbuseStore {
224
224
 
225
225
  /** Evicts the oldest entries of `map` down to `maxKeys` in a single pass so
226
226
  * an adversarial fan-out over unique identities cannot pin memory. Deleting
227
- * one entry at a time was O(1) but starved reclaim. */
227
+ * one entry at a time is O(1) but starves reclaim. */
228
228
  private evictOldest<K, V>(map: Map<K, V>): void {
229
229
  if (map.size < this.maxKeys) return;
230
230
  let toEvict = map.size - this.maxKeys + 1;
@@ -251,10 +251,10 @@ export class InMemoryIpAbuseStore implements IpAbuseStore {
251
251
  /**
252
252
  * Outage injection for tests, mirroring the store's own reachability.
253
253
  *
254
- * The service no longer probes the store before using it: every method below
254
+ * The service does not probe the store before using it: every method below
255
255
  * throws when the store is unreachable, and the service's catch maps that to
256
- * a bounded `admission_unavailable`. Keeping a separate probe would only add
257
- * a round trip to every request on the Redis store.
256
+ * a bounded `admission_unavailable`. A separate probe would only add a round
257
+ * trip to every request on the Redis store.
258
258
  */
259
259
  private assertAvailable(): void {
260
260
  if (this.failing) throw new Error("ip store unavailable");
@@ -421,7 +421,12 @@ export class IpAbuseProtectionService {
421
421
 
422
422
  if (outcome.banned) {
423
423
  throw new GatewayError("quota_exceeded", 429, "This address is temporarily banned for too many failed requests. Wait for the ban to lift, then slow down.", {
424
- reason: "admission-unavailable",
424
+ // Distinguishes this from the rate-limited 429 below and from an API
425
+ // key's own budget. It used to be "admission-unavailable", which is
426
+ // the label for an *unreachable* store — so every refusal from this
427
+ // layer claimed an outage that had not happened, and a client could
428
+ // not tell "banned" from "over the window" from "store is down".
429
+ reason: "ip-banned",
425
430
  ip,
426
431
  route: input.route,
427
432
  // Measured by the store: the ban marker's remaining TTL. Carried as
@@ -433,7 +438,7 @@ export class IpAbuseProtectionService {
433
438
 
434
439
  if (outcome.count > this.maxRequestsPerWindow) {
435
440
  throw new GatewayError("quota_exceeded", 429, "This address is sending requests faster than the abuse limit allows. Back off and retry after the window rolls over.", {
436
- reason: "admission-unavailable",
441
+ reason: "ip-rate-limited",
437
442
  ip,
438
443
  route: input.route,
439
444
  count: outcome.count,