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
@@ -1,15 +1,17 @@
1
1
  /**
2
- * DeepSeek sometimes emits its native tool markup as assistant text when the
3
- * structured tool channel was dropped in transit. A complete, well-formed block
4
- * becomes ordinary tool calls; anything incomplete stays text.
2
+ * CodeBuddy-family DeepSeek sometimes emits native tool markup as assistant text.
3
+ * Complete declared calls are recovered; incomplete and undeclared blocks stay text.
5
4
  */
6
-
7
- const PIPE = "\uFF5C\uFF5C";
8
- const CALLS = new RegExp(`<${PIPE}DSML${PIPE}\\s+calls>([\\s\\S]*?)</${PIPE}DSML${PIPE}\\s+calls>`, "i");
9
- const INVOKE = new RegExp(`<${PIPE}DSML${PIPE}\\s+invoke\\s+name=["']([^"']+)["']>([\\s\\S]*?)</${PIPE}DSML${PIPE}\\s+invoke>`, "gi");
10
- const PARAMETER = new RegExp(`<${PIPE}DSML${PIPE}\\s+parameter\\s+name=["']([^"']+)["'][^>]*>([\\s\\S]*?)</${PIPE}DSML${PIPE}\\s+parameter>`, "gi");
5
+ const SEPARATORS = ["||", "|", "|"] as const;
6
+ const BLOCK_NAMES = ["calls", "function_calls"] as const;
7
+ const OPENERS = SEPARATORS.flatMap((separator) => BLOCK_NAMES.map((name) => `<${separator}DSML${separator} ${name}>`));
11
8
  const TOOL_NAME = /^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,63}$/;
12
9
 
10
+ /** Escapes regex metacharacters so a `|` separator is literal, not alternation. */
11
+ function escapeRegExp(value: string): string {
12
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
13
+ }
14
+
13
15
  export interface DsmlToolCall {
14
16
  readonly id: string;
15
17
  readonly name: string;
@@ -21,27 +23,137 @@ export interface DsmlRecovery {
21
23
  readonly calls: readonly DsmlToolCall[];
22
24
  }
23
25
 
24
- export function parseDsmlToolCalls(text: string): DsmlRecovery | undefined {
26
+ export class DsmlToolCallParser {
27
+ readonly #allowedNames: ReadonlySet<string>;
28
+ #pending = "";
29
+ #callSequence = 0;
30
+
31
+ constructor(allowedNames: ReadonlySet<string>) {
32
+ this.#allowedNames = allowedNames;
33
+ }
34
+
35
+ push(chunk: string): DsmlRecovery {
36
+ this.#pending += chunk;
37
+ let cursor = 0;
38
+ let visible = "";
39
+ const calls: DsmlToolCall[] = [];
40
+ for (;;) {
41
+ let openIndex = -1;
42
+ let opener = "";
43
+ for (const candidate of OPENERS) {
44
+ const index = this.#pending.indexOf(candidate, cursor);
45
+ if (index >= 0 && (openIndex < 0 || index < openIndex)) {
46
+ openIndex = index;
47
+ opener = candidate;
48
+ }
49
+ }
50
+ if (openIndex < 0) {
51
+ const safeEnd = this.#settledEnd(cursor);
52
+ visible += this.#pending.slice(cursor, safeEnd);
53
+ cursor = safeEnd;
54
+ break;
55
+ }
56
+ visible += this.#pending.slice(cursor, openIndex);
57
+ const separator = SEPARATORS.find((value) => opener.startsWith(`<${value}DSML`));
58
+ const blockName = BLOCK_NAMES.find((value) => opener.endsWith(` ${value}>`));
59
+ if (separator === undefined || blockName === undefined) {
60
+ visible += "<";
61
+ cursor = openIndex + 1;
62
+ continue;
63
+ }
64
+ const close = `</${separator}DSML${separator} ${blockName}>`;
65
+ const closeIndex = this.#pending.indexOf(close, openIndex + opener.length);
66
+ if (closeIndex < 0) {
67
+ cursor = openIndex;
68
+ break;
69
+ }
70
+ const end = closeIndex + close.length;
71
+ const block = this.#pending.slice(openIndex, end);
72
+ const parsed = parseBlock(block, opener, close, separator, this.#allowedNames);
73
+ if (parsed === undefined) visible += block;
74
+ else {
75
+ for (const call of parsed) calls.push({ ...call, id: `call_dsml_${this.#callSequence++}_${call.name}` });
76
+ }
77
+ cursor = end;
78
+ }
79
+ this.#pending = this.#pending.slice(cursor);
80
+ return { text: visible, calls };
81
+ }
82
+
83
+ flush(): DsmlRecovery {
84
+ const text = this.#pending;
85
+ this.#pending = "";
86
+ return { text, calls: [] };
87
+ }
88
+
89
+ #settledEnd(start: number): number {
90
+ const remaining = this.#pending.slice(start);
91
+ for (let length = Math.min(OPENERS.reduce((max, opener) => Math.max(max, opener.length - 1), 0), remaining.length); length > 0; length -= 1) {
92
+ const suffix = remaining.slice(-length);
93
+ if (OPENERS.some((opener) => opener.startsWith(suffix))) return this.#pending.length - length;
94
+ }
95
+ return this.#pending.length;
96
+ }
97
+ }
98
+
99
+ export function parseDsmlToolCalls(
100
+ text: string,
101
+ allowedNames: ReadonlySet<string> = new Set(),
102
+ ): DsmlRecovery | undefined {
25
103
  if (!text.includes("DSML")) return undefined;
26
- const block = CALLS.exec(text);
27
- if (block === null || block[1] === undefined || block.index === undefined) return undefined;
28
- const calls: DsmlToolCall[] = [];
29
- for (const invoke of block[1].matchAll(INVOKE)) {
30
- const name = invoke[1]?.trim() ?? "";
31
- if (!TOOL_NAME.test(name)) return undefined;
32
- const args: Record<string, string> = {};
33
- for (const parameter of (invoke[2] ?? "").matchAll(PARAMETER)) {
34
- const key = parameter[1]?.trim();
35
- if (key === undefined || key.length === 0) return undefined;
36
- args[key] = (parameter[2] ?? "").trim();
104
+ const parser = new DsmlToolCallParser(allowedNames);
105
+ const first = parser.push(text);
106
+ const last = parser.flush();
107
+ const recovered = [...first.calls, ...last.calls];
108
+ return recovered.length === 0 ? undefined : { text: `${first.text}${last.text}`.trim(), calls: recovered };
109
+ }
110
+
111
+ function parseBlock(
112
+ block: string,
113
+ opener: string,
114
+ close: string,
115
+ separator: string,
116
+ allowedNames: ReadonlySet<string>,
117
+ ): Omit<DsmlToolCall, "id">[] | undefined {
118
+ const inner = block.slice(opener.length, -close.length);
119
+ const pipe = escapeRegExp(separator);
120
+ const invokePattern = new RegExp(`<${pipe}DSML${pipe}\\s+invoke\\s+name="([^"]*)"\\s*>`, "g");
121
+ const calls: Omit<DsmlToolCall, "id">[] = [];
122
+ // The allowlist is strict only when the caller knows the declared tools: a
123
+ // caller without one (a direct parse) keeps the generic name rule, while a
124
+ // dispatch that declared its tools must never execute a name it did not offer.
125
+ const strict = allowedNames.size > 0;
126
+ let cursor = 0;
127
+ for (const invoke of inner.matchAll(invokePattern)) {
128
+ const name = invoke[1] ?? "";
129
+ if (!TOOL_NAME.test(name) || (strict && !allowedNames.has(name)) || invoke.index === undefined) return undefined;
130
+ if (inner.slice(cursor, invoke.index).trim().length > 0) return undefined;
131
+ const bodyStart = invoke.index + invoke[0].length;
132
+ const invokeClose = `</${separator}DSML${separator} invoke>`;
133
+ const invokeEnd = inner.indexOf(invokeClose, bodyStart);
134
+ if (invokeEnd < 0) return undefined;
135
+ const parameterPattern = new RegExp(`<${pipe}DSML${pipe}\\s+parameter\\s+name="([^"]*)"(?:\\s+string="(true|false)")?\\s*>`, "g");
136
+ const body = inner.slice(bodyStart, invokeEnd);
137
+ const args: Record<string, unknown> = {};
138
+ let parameterCursor = 0;
139
+ for (const parameter of body.matchAll(parameterPattern)) {
140
+ const key = parameter[1] ?? "";
141
+ if (key.length === 0 || parameter.index === undefined || body.slice(parameterCursor, parameter.index).trim().length > 0) return undefined;
142
+ const valueStart = parameter.index + parameter[0].length;
143
+ const parameterClose = `</${separator}DSML${separator} parameter>`;
144
+ const valueEnd = body.indexOf(parameterClose, valueStart);
145
+ if (valueEnd < 0) return undefined;
146
+ const raw = body.slice(valueStart, valueEnd);
147
+ if (parameter[2] === "true") args[key] = raw;
148
+ else {
149
+ try { args[key] = JSON.parse(raw); }
150
+ catch { args[key] = raw; }
151
+ }
152
+ parameterCursor = valueEnd + parameterClose.length;
37
153
  }
38
- calls.push({
39
- id: `call_dsml_${calls.length}_${name}`,
40
- name,
41
- arguments: JSON.stringify(args),
42
- });
154
+ if (body.slice(parameterCursor).trim().length > 0) return undefined;
155
+ calls.push({ name, arguments: JSON.stringify(args) });
156
+ cursor = invokeEnd + invokeClose.length;
43
157
  }
44
- if (calls.length === 0) return undefined;
45
- const visible = `${text.slice(0, block.index)}${text.slice(block.index + block[0].length)}`.trim();
46
- return { text: visible, calls };
158
+ return calls.length > 0 && inner.slice(cursor).trim().length === 0 ? calls : undefined;
47
159
  }
@@ -182,7 +182,10 @@ export function parseResponsesResponseToEvents(
182
182
  }
183
183
  : { payload: text }),
184
184
  ...(position === 0 && encryptedContent !== undefined
185
- ? { encrypted_content: encryptedContent }
185
+ ? {
186
+ encrypted_content: encryptedContent,
187
+ ...(itemId === undefined ? {} : { item_id: itemId }),
188
+ }
186
189
  : {}),
187
190
  },
188
191
  });
@@ -342,9 +345,9 @@ export async function* decodeResponsesSseStream(
342
345
  const eventType = event["type"];
343
346
  // An explicit failure inside a 200 OK. Two shapes carry one: a top-level
344
347
  // `type: "error"` frame (the error object itself), and a `response.failed`
345
- // terminal whose `response.error` explains why. Both used to be recorded
346
- // only as a `failed` terminal, so the client got no code and telemetry got
347
- // `unknown_error`; the Claude and Gemini decoders already raised.
348
+ // terminal whose `response.error` explains why. Recording either only as a
349
+ // `failed` terminal would leave the client with no code and telemetry with
350
+ // `unknown_error`; the Claude and Gemini decoders already raise.
348
351
  const failedResponse = eventType === "response.failed" && isRecord(event["response"]) ? event["response"] : undefined;
349
352
  const streamError = gatewayErrorFromStreamError(
350
353
  eventType === "error" ? event : failedResponse?.["error"],
@@ -566,7 +569,10 @@ export async function* decodeResponsesSseStream(
566
569
  ? { payload: null, summary: text, summary_index: reasoningPart.index }
567
570
  : { payload: text }),
568
571
  ...(position === 0 && encryptedContent !== undefined
569
- ? { encrypted_content: encryptedContent }
572
+ ? {
573
+ encrypted_content: encryptedContent,
574
+ ...(itemId === undefined ? {} : { item_id: itemId }),
575
+ }
570
576
  : {}),
571
577
  },
572
578
  } as CanonicalEvent;
@@ -2,11 +2,10 @@
2
2
  * OpenAI-family in-stream error frames → typed `GatewayError`.
3
3
  *
4
4
  * A `200 OK` whose SSE body carries an explicit error envelope is an upstream
5
- * failure that arrived *after* the status line. The chat, responses, and codex
6
- * decoders used to record it only as a terminal `state: "failed"`, which left
7
- * the client with no `error.code`, no message, and telemetry with the
8
- * `unknown_error` category — while the Claude (`messages.ts`) and Gemini
9
- * decoders already threw for the identical event. This module is the one
5
+ * failure that arrived *after* the status line. Recording it only as a terminal
6
+ * `state: "failed"` leaves the client with no `error.code`, no message, and
7
+ * telemetry with the `unknown_error` category — while the Claude (`messages.ts`)
8
+ * and Gemini decoders throw for the identical event. This module is the one
10
9
  * classifier those surfaces share.
11
10
  *
12
11
  * Classification reads **structured identifiers only** (`error.type`,
@@ -207,6 +207,9 @@ export class OpenAICompatibleAdapter extends BaseProviderAdapter {
207
207
  ...(this.config.chat_token_limit_field === undefined
208
208
  ? {}
209
209
  : { chatTokenLimitField: this.config.chat_token_limit_field }),
210
+ ...(context.image_clamp_max_bytes === undefined
211
+ ? {}
212
+ : { maxBytesOverride: context.image_clamp_max_bytes }),
210
213
  });
211
214
  if (
212
215
  wireFamily !== "messages" &&
@@ -236,14 +236,13 @@ function jsonResponse(body: unknown): Response {
236
236
  *
237
237
  * Key fetching, caching, in-flight deduplication, the minimum refresh interval
238
238
  * on an unknown `kid`, and `kid`-rotation pickup are all delegated to
239
- * `jose`'s `createRemoteJWKSet` — the same algorithm this class used to
240
- * reimplement by hand. `jose` additionally honours each JWK's `use` and
241
- * `key_ops`, so an encryption key published alongside the signing keys cannot
242
- * be selected for verification.
239
+ * `jose`'s `createRemoteJWKSet`. `jose` additionally honours each JWK's `use`
240
+ * and `key_ops`, so an encryption key published alongside the signing keys
241
+ * cannot be selected for verification.
243
242
  *
244
243
  * The algorithm is checked against `SUPPORTED_ALGORITHMS` *before* any key is
245
- * resolved, so the asymmetric-only allowlist (no `none`, no HMAC) is
246
- * unchanged by the migration.
244
+ * resolved, so the asymmetric-only allowlist (no `none`, no HMAC) holds
245
+ * regardless of what the JWKS publishes.
247
246
  */
248
247
  export class JwksVerifier {
249
248
  readonly #keys: RemoteJWKSet;
@@ -317,10 +316,10 @@ export class JwksVerifier {
317
316
  /**
318
317
  * One verifier per URL, bounded and time-limited.
319
318
  *
320
- * The registry used to be an unbounded, never-evicted `Map`: every JWKS URL
321
- * ever seen pinned its verifier — and its key cache — for the process
322
- * lifetime. A TTL plus a hard entry cap keeps a fleet of issuers from growing
323
- * it without limit while still sharing a verifier's key cache across calls.
319
+ * An unbounded, never-evicted registry would pin every JWKS URL ever seen —
320
+ * and its verifier's key cache — for the process lifetime. A TTL plus a hard
321
+ * entry cap keeps a fleet of issuers from growing it without limit while still
322
+ * sharing a verifier's key cache across calls.
324
323
  */
325
324
  const JWKS_VERIFIER_TTL_MS = 5 * 60 * 1_000;
326
325
  const JWKS_VERIFIER_MAX_ENTRIES = 64;
@@ -23,6 +23,7 @@
23
23
  import { randomUUID } from "node:crypto";
24
24
  import { and, eq, inArray, isNull, lt, or, sql } from "drizzle-orm";
25
25
  import type { CartethyiaDatabase } from "../../persistence/db-handle";
26
+ import { sleep } from "../../shared/sleep";
26
27
  import { providerAccounts, providerOauthStates } from "../../persistence/schema";
27
28
  import { decryptCredentialToString, encryptCredential } from "../../security/crypto";
28
29
  import { invalidateCredentialCache } from "../operations/provider-credential-service";
@@ -258,6 +259,7 @@ async function disableAccount(
258
259
  )
259
260
  .returning({ id: providerOauthStates.providerAccountId });
260
261
  if (leaseRows.length === 0) return;
262
+ const disabledRecoveryMs = resolveAccountDisabledRecoveryMs();
261
263
  await tx
262
264
  .update(providerAccounts)
263
265
  .set({
@@ -271,13 +273,13 @@ async function disableAccount(
271
273
  lastErrorCategory: "auth_invalidated",
272
274
  lastErrorAt: new Date(),
273
275
  // Carries a recovery deadline so `sweepExpiredCooldowns` can bring this
274
- // account back once it elapses. `disabled` used to be terminal, which
275
- // parked a credential for good on one definitive rejection; the deadline
276
- // is also what distinguishes this (machine-disabled) from an operator's
277
- // own disable, which leaves `cooldownUntil` null and is therefore never
278
- // swept. See `resolveAccountDisabledRecoveryMs`.
279
- ...(resolveAccountDisabledRecoveryMs() > 0
280
- ? { cooldownUntil: new Date(Date.now() + resolveAccountDisabledRecoveryMs()) }
276
+ // account back once it elapses; without one, one definitive rejection
277
+ // parks the credential for good. The deadline is also what distinguishes
278
+ // this (machine-disabled) from an operator's own disable, which leaves
279
+ // `cooldownUntil` null and is therefore never swept. See
280
+ // `resolveAccountDisabledRecoveryMs`.
281
+ ...(disabledRecoveryMs > 0
282
+ ? { cooldownUntil: new Date(Date.now() + disabledRecoveryMs) }
281
283
  : {}),
282
284
  })
283
285
  .where(eq(providerAccounts.id, accountId));
@@ -311,10 +313,6 @@ async function markStaticToken(db: CartethyiaDatabase, accountId: string): Promi
311
313
  .where(eq(providerAccounts.id, accountId));
312
314
  }
313
315
 
314
- function sleep(ms: number): Promise<void> {
315
- return new Promise((resolve) => setTimeout(resolve, ms));
316
- }
317
-
318
316
  function failureMessage(error: unknown): string {
319
317
  if (error instanceof Error) return error.message;
320
318
  return String(error);
@@ -1,119 +1,122 @@
1
- import type { ProviderModule } from "./provider-registry";
2
- import {
3
- GENERIC_API_KEY_SPECS,
4
- VERSIONED_ROOT_API_KEY_SPECS,
5
- } from "./configured-openai-providers";
6
- import { createApiKeyAdapter, type ApiKeyProviderSpec } from "./configured-provider";
7
-
8
- /**
9
- * Providers whose adapter and catalog discovery are the same bearer-auth
10
- * OpenAI-compatible contract. Providers with static catalogs or additional
11
- * upstream behavior remain declared beside their special handling.
12
- */
13
- export const DISCOVERY_BACKED_PROVIDER_IDS = [
14
- "groq",
15
- "together",
16
- "deepinfra",
17
- "siliconflow",
18
- "siliconflowcn",
19
- "novita",
20
- "venice",
21
- "nanogpt",
22
- "baseten",
23
- "synthetic",
24
- "stepfun",
25
- "sakana",
26
- "helmcode",
27
- "coralbricks",
28
- "aiand",
29
- "wafer",
30
- "firepass",
31
- "zhipucoding",
32
- "dashscope",
33
- "qianfan",
34
- ] as const;
35
-
36
- export type DiscoveryBackedProviderId = (typeof DISCOVERY_BACKED_PROVIDER_IDS)[number];
37
-
38
- type DiscoveryBackedCapabilities = Pick<
39
- ProviderModule,
40
- "endpointPathsByWireFamily" | "loadAdapter" | "loadModelDiscovery"
41
- >;
42
-
43
- type ModelDiscoveryFactory = NonNullable<ProviderModule["loadModelDiscovery"]>;
44
- type OpenAIModelDiscoveryFactory = (
45
- providerId: string,
46
- options?: {
47
- readonly headers?: (credential: string) => Readonly<Record<string, string>>;
48
- },
49
- ) => ModelDiscoveryFactory;
50
-
51
- const DISCOVERY_BACKED_PROVIDER_SPECS = {
52
- groq: VERSIONED_ROOT_API_KEY_SPECS.groq,
53
- together: GENERIC_API_KEY_SPECS.together,
54
- deepinfra: VERSIONED_ROOT_API_KEY_SPECS.deepinfra,
55
- siliconflow: GENERIC_API_KEY_SPECS.siliconflow,
56
- siliconflowcn: GENERIC_API_KEY_SPECS.siliconflowcn,
57
- novita: VERSIONED_ROOT_API_KEY_SPECS.novita,
58
- venice: VERSIONED_ROOT_API_KEY_SPECS.venice,
59
- nanogpt: GENERIC_API_KEY_SPECS.nanogpt,
60
- baseten: GENERIC_API_KEY_SPECS.baseten,
61
- synthetic: GENERIC_API_KEY_SPECS.synthetic,
62
- stepfun: GENERIC_API_KEY_SPECS.stepfun,
63
- sakana: GENERIC_API_KEY_SPECS.sakana,
64
- helmcode: GENERIC_API_KEY_SPECS.helmcode,
65
- coralbricks: GENERIC_API_KEY_SPECS.coralbricks,
66
- aiand: GENERIC_API_KEY_SPECS.aiand,
67
- wafer: GENERIC_API_KEY_SPECS.wafer,
68
- firepass: VERSIONED_ROOT_API_KEY_SPECS.firepass,
69
- zhipucoding: VERSIONED_ROOT_API_KEY_SPECS.zhipucoding,
70
- dashscope: GENERIC_API_KEY_SPECS.dashscope,
71
- qianfan: VERSIONED_ROOT_API_KEY_SPECS.qianfan,
72
- } satisfies Readonly<Record<DiscoveryBackedProviderId, ApiKeyProviderSpec>>;
73
-
74
- function discoveryBackedCapabilities(
75
- providerId: DiscoveryBackedProviderId,
76
- spec: ApiKeyProviderSpec,
77
- openAIModelDiscovery: OpenAIModelDiscoveryFactory,
78
- ): DiscoveryBackedCapabilities {
79
- return {
80
- ...(spec.endpoint_paths_by_wire_family === undefined
81
- ? {}
82
- : { endpointPathsByWireFamily: spec.endpoint_paths_by_wire_family }),
83
- loadAdapter: async () => createApiKeyAdapter(spec),
84
- loadModelDiscovery: openAIModelDiscovery(providerId, {
85
- headers: (credential) => ({ authorization: `Bearer ${credential}` }),
86
- }),
87
- };
88
- }
89
-
90
- /**
91
- * Creates the standard provider capability table using the registry's shared
92
- * discovery loader, keeping its lazy import and authentication behavior intact.
93
- */
94
- export function createDiscoveryBackedProviderCapabilities(
95
- openAIModelDiscovery: OpenAIModelDiscoveryFactory,
96
- ): Readonly<Record<DiscoveryBackedProviderId, DiscoveryBackedCapabilities>> {
97
- return {
98
- groq: discoveryBackedCapabilities("groq", DISCOVERY_BACKED_PROVIDER_SPECS.groq, openAIModelDiscovery),
99
- together: discoveryBackedCapabilities("together", DISCOVERY_BACKED_PROVIDER_SPECS.together, openAIModelDiscovery),
100
- deepinfra: discoveryBackedCapabilities("deepinfra", DISCOVERY_BACKED_PROVIDER_SPECS.deepinfra, openAIModelDiscovery),
101
- siliconflow: discoveryBackedCapabilities("siliconflow", DISCOVERY_BACKED_PROVIDER_SPECS.siliconflow, openAIModelDiscovery),
102
- siliconflowcn: discoveryBackedCapabilities("siliconflowcn", DISCOVERY_BACKED_PROVIDER_SPECS.siliconflowcn, openAIModelDiscovery),
103
- novita: discoveryBackedCapabilities("novita", DISCOVERY_BACKED_PROVIDER_SPECS.novita, openAIModelDiscovery),
104
- venice: discoveryBackedCapabilities("venice", DISCOVERY_BACKED_PROVIDER_SPECS.venice, openAIModelDiscovery),
105
- nanogpt: discoveryBackedCapabilities("nanogpt", DISCOVERY_BACKED_PROVIDER_SPECS.nanogpt, openAIModelDiscovery),
106
- baseten: discoveryBackedCapabilities("baseten", DISCOVERY_BACKED_PROVIDER_SPECS.baseten, openAIModelDiscovery),
107
- synthetic: discoveryBackedCapabilities("synthetic", DISCOVERY_BACKED_PROVIDER_SPECS.synthetic, openAIModelDiscovery),
108
- stepfun: discoveryBackedCapabilities("stepfun", DISCOVERY_BACKED_PROVIDER_SPECS.stepfun, openAIModelDiscovery),
109
- sakana: discoveryBackedCapabilities("sakana", DISCOVERY_BACKED_PROVIDER_SPECS.sakana, openAIModelDiscovery),
110
- helmcode: discoveryBackedCapabilities("helmcode", DISCOVERY_BACKED_PROVIDER_SPECS.helmcode, openAIModelDiscovery),
111
- coralbricks: discoveryBackedCapabilities("coralbricks", DISCOVERY_BACKED_PROVIDER_SPECS.coralbricks, openAIModelDiscovery),
112
- aiand: discoveryBackedCapabilities("aiand", DISCOVERY_BACKED_PROVIDER_SPECS.aiand, openAIModelDiscovery),
113
- wafer: discoveryBackedCapabilities("wafer", DISCOVERY_BACKED_PROVIDER_SPECS.wafer, openAIModelDiscovery),
114
- firepass: discoveryBackedCapabilities("firepass", DISCOVERY_BACKED_PROVIDER_SPECS.firepass, openAIModelDiscovery),
115
- zhipucoding: discoveryBackedCapabilities("zhipucoding", DISCOVERY_BACKED_PROVIDER_SPECS.zhipucoding, openAIModelDiscovery),
116
- dashscope: discoveryBackedCapabilities("dashscope", DISCOVERY_BACKED_PROVIDER_SPECS.dashscope, openAIModelDiscovery),
117
- qianfan: discoveryBackedCapabilities("qianfan", DISCOVERY_BACKED_PROVIDER_SPECS.qianfan, openAIModelDiscovery),
118
- };
119
- }
1
+ import type { ProviderModule } from "./provider-registry";
2
+ import {
3
+ GENERIC_API_KEY_SPECS,
4
+ VERSIONED_ROOT_API_KEY_SPECS,
5
+ } from "./configured-openai-providers";
6
+ import { createApiKeyAdapter, type ApiKeyProviderSpec } from "./configured-provider";
7
+
8
+ /**
9
+ * Providers whose adapter and catalog discovery are the same bearer-auth
10
+ * OpenAI-compatible contract. Providers with static catalogs or additional
11
+ * upstream behavior remain declared beside their special handling.
12
+ */
13
+ export const DISCOVERY_BACKED_PROVIDER_IDS = [
14
+ "groq",
15
+ "together",
16
+ "deepinfra",
17
+ "siliconflow",
18
+ "siliconflowcn",
19
+ "novita",
20
+ "larprouter",
21
+ "venice",
22
+ "nanogpt",
23
+ "baseten",
24
+ "synthetic",
25
+ "stepfun",
26
+ "sakana",
27
+ "helmcode",
28
+ "coralbricks",
29
+ "aiand",
30
+ "wafer",
31
+ "firepass",
32
+ "zhipucoding",
33
+ "dashscope",
34
+ "qianfan",
35
+ ] as const;
36
+
37
+ export type DiscoveryBackedProviderId = (typeof DISCOVERY_BACKED_PROVIDER_IDS)[number];
38
+
39
+ type DiscoveryBackedCapabilities = Pick<
40
+ ProviderModule,
41
+ "endpointPathsByWireFamily" | "loadAdapter" | "loadModelDiscovery"
42
+ >;
43
+
44
+ type ModelDiscoveryFactory = NonNullable<ProviderModule["loadModelDiscovery"]>;
45
+ type OpenAIModelDiscoveryFactory = (
46
+ providerId: string,
47
+ options?: {
48
+ readonly headers?: (credential: string) => Readonly<Record<string, string>>;
49
+ },
50
+ ) => ModelDiscoveryFactory;
51
+
52
+ const DISCOVERY_BACKED_PROVIDER_SPECS = {
53
+ groq: VERSIONED_ROOT_API_KEY_SPECS.groq,
54
+ larprouter: GENERIC_API_KEY_SPECS.larprouter,
55
+ together: GENERIC_API_KEY_SPECS.together,
56
+ deepinfra: VERSIONED_ROOT_API_KEY_SPECS.deepinfra,
57
+ siliconflow: GENERIC_API_KEY_SPECS.siliconflow,
58
+ siliconflowcn: GENERIC_API_KEY_SPECS.siliconflowcn,
59
+ novita: VERSIONED_ROOT_API_KEY_SPECS.novita,
60
+ venice: VERSIONED_ROOT_API_KEY_SPECS.venice,
61
+ nanogpt: GENERIC_API_KEY_SPECS.nanogpt,
62
+ baseten: GENERIC_API_KEY_SPECS.baseten,
63
+ synthetic: GENERIC_API_KEY_SPECS.synthetic,
64
+ stepfun: GENERIC_API_KEY_SPECS.stepfun,
65
+ sakana: GENERIC_API_KEY_SPECS.sakana,
66
+ helmcode: GENERIC_API_KEY_SPECS.helmcode,
67
+ coralbricks: GENERIC_API_KEY_SPECS.coralbricks,
68
+ aiand: GENERIC_API_KEY_SPECS.aiand,
69
+ wafer: GENERIC_API_KEY_SPECS.wafer,
70
+ firepass: VERSIONED_ROOT_API_KEY_SPECS.firepass,
71
+ zhipucoding: VERSIONED_ROOT_API_KEY_SPECS.zhipucoding,
72
+ dashscope: GENERIC_API_KEY_SPECS.dashscope,
73
+ qianfan: VERSIONED_ROOT_API_KEY_SPECS.qianfan,
74
+ } satisfies Readonly<Record<DiscoveryBackedProviderId, ApiKeyProviderSpec>>;
75
+
76
+ function discoveryBackedCapabilities(
77
+ providerId: DiscoveryBackedProviderId,
78
+ spec: ApiKeyProviderSpec,
79
+ openAIModelDiscovery: OpenAIModelDiscoveryFactory,
80
+ ): DiscoveryBackedCapabilities {
81
+ return {
82
+ ...(spec.endpoint_paths_by_wire_family === undefined
83
+ ? {}
84
+ : { endpointPathsByWireFamily: spec.endpoint_paths_by_wire_family }),
85
+ loadAdapter: async () => createApiKeyAdapter(spec),
86
+ loadModelDiscovery: openAIModelDiscovery(providerId, {
87
+ headers: (credential) => ({ authorization: `Bearer ${credential}` }),
88
+ }),
89
+ };
90
+ }
91
+
92
+ /**
93
+ * Creates the standard provider capability table using the registry's shared
94
+ * discovery loader, keeping its lazy import and authentication behavior intact.
95
+ */
96
+ export function createDiscoveryBackedProviderCapabilities(
97
+ openAIModelDiscovery: OpenAIModelDiscoveryFactory,
98
+ ): Readonly<Record<DiscoveryBackedProviderId, DiscoveryBackedCapabilities>> {
99
+ return {
100
+ groq: discoveryBackedCapabilities("groq", DISCOVERY_BACKED_PROVIDER_SPECS.groq, openAIModelDiscovery),
101
+ together: discoveryBackedCapabilities("together", DISCOVERY_BACKED_PROVIDER_SPECS.together, openAIModelDiscovery),
102
+ deepinfra: discoveryBackedCapabilities("deepinfra", DISCOVERY_BACKED_PROVIDER_SPECS.deepinfra, openAIModelDiscovery),
103
+ siliconflow: discoveryBackedCapabilities("siliconflow", DISCOVERY_BACKED_PROVIDER_SPECS.siliconflow, openAIModelDiscovery),
104
+ siliconflowcn: discoveryBackedCapabilities("siliconflowcn", DISCOVERY_BACKED_PROVIDER_SPECS.siliconflowcn, openAIModelDiscovery),
105
+ novita: discoveryBackedCapabilities("novita", DISCOVERY_BACKED_PROVIDER_SPECS.novita, openAIModelDiscovery),
106
+ larprouter: discoveryBackedCapabilities("larprouter", DISCOVERY_BACKED_PROVIDER_SPECS.larprouter, openAIModelDiscovery),
107
+ venice: discoveryBackedCapabilities("venice", DISCOVERY_BACKED_PROVIDER_SPECS.venice, openAIModelDiscovery),
108
+ nanogpt: discoveryBackedCapabilities("nanogpt", DISCOVERY_BACKED_PROVIDER_SPECS.nanogpt, openAIModelDiscovery),
109
+ baseten: discoveryBackedCapabilities("baseten", DISCOVERY_BACKED_PROVIDER_SPECS.baseten, openAIModelDiscovery),
110
+ synthetic: discoveryBackedCapabilities("synthetic", DISCOVERY_BACKED_PROVIDER_SPECS.synthetic, openAIModelDiscovery),
111
+ stepfun: discoveryBackedCapabilities("stepfun", DISCOVERY_BACKED_PROVIDER_SPECS.stepfun, openAIModelDiscovery),
112
+ sakana: discoveryBackedCapabilities("sakana", DISCOVERY_BACKED_PROVIDER_SPECS.sakana, openAIModelDiscovery),
113
+ helmcode: discoveryBackedCapabilities("helmcode", DISCOVERY_BACKED_PROVIDER_SPECS.helmcode, openAIModelDiscovery),
114
+ coralbricks: discoveryBackedCapabilities("coralbricks", DISCOVERY_BACKED_PROVIDER_SPECS.coralbricks, openAIModelDiscovery),
115
+ aiand: discoveryBackedCapabilities("aiand", DISCOVERY_BACKED_PROVIDER_SPECS.aiand, openAIModelDiscovery),
116
+ wafer: discoveryBackedCapabilities("wafer", DISCOVERY_BACKED_PROVIDER_SPECS.wafer, openAIModelDiscovery),
117
+ firepass: discoveryBackedCapabilities("firepass", DISCOVERY_BACKED_PROVIDER_SPECS.firepass, openAIModelDiscovery),
118
+ zhipucoding: discoveryBackedCapabilities("zhipucoding", DISCOVERY_BACKED_PROVIDER_SPECS.zhipucoding, openAIModelDiscovery),
119
+ dashscope: discoveryBackedCapabilities("dashscope", DISCOVERY_BACKED_PROVIDER_SPECS.dashscope, openAIModelDiscovery),
120
+ qianfan: discoveryBackedCapabilities("qianfan", DISCOVERY_BACKED_PROVIDER_SPECS.qianfan, openAIModelDiscovery),
121
+ };
122
+ }
@@ -16,6 +16,7 @@ import type { ProviderId } from "./provider-contracts";
16
16
  * pairs this spec with `openAIModelDiscovery`.
17
17
  */
18
18
  export const GENERIC_API_KEY_PROVIDER_IDS = [
19
+ "larprouter",
19
20
  "mistral",
20
21
  "fireworks",
21
22
  "nvidia",
@@ -104,14 +105,13 @@ export type GenericApiKeyProviderId = (typeof GENERIC_API_KEY_PROVIDER_IDS)[numb
104
105
 
105
106
  function genericApiKeySpec(providerId: GenericApiKeyProviderId): ApiKeyProviderSpec {
106
107
  // These roots are *not* bare hosts — every one of them already ends at `/v1`
107
- // — so the path is declared below that root. It used to be pinned from
108
- // `BARE_ROOT_ENDPOINTS` and silently corrected at dispatch, which meant the
109
- // declared path was wrong for all of them and nothing said so. Both spec
110
- // shapes now declare the same thing, so this is the one implementation.
108
+ // — so the path is declared below that root. Both spec shapes declare the same
109
+ // thing, so this is the one implementation.
111
110
  return versionedRootApiKeySpec(providerId);
112
111
  }
113
112
 
114
113
  export const GENERIC_API_KEY_SPECS = {
114
+ larprouter: genericApiKeySpec("larprouter"),
115
115
  mistral: genericApiKeySpec("mistral"),
116
116
  fireworks: genericApiKeySpec("fireworks"),
117
117
  nvidia: genericApiKeySpec("nvidia"),