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
@@ -0,0 +1,218 @@
1
+ // Grok's two quota numbers, and why they disagree.
2
+ //
3
+ // The CLI plane answers every response with a token bucket:
4
+ //
5
+ // x-ratelimit-limit-tokens: 1000000
6
+ // x-ratelimit-remaining-tokens: 996169
7
+ // x-ratelimit-remaining-requests: 20
8
+ //
9
+ // and that bucket is *eventually consistent*. Right after a request it can
10
+ // still read a full `1000000` and only drop later. Worse, the figure it
11
+ // advertises is not the figure that binds: the real free allowance for a model
12
+ // like `grok-4.6` is 600k tokens per rolling 24 hours, and the only place
13
+ // upstream states it is the body of a 429:
14
+ //
15
+ // {"code":"subscription:free-usage-exhausted",
16
+ // "error":"... tokens (actual/limit): 603024/600000 ... Usage resets over a
17
+ // rolling 24-hour window"}
18
+ //
19
+ // So there are two readings with different authority. The header is available
20
+ // on every call and is the coarse indicator; the 429 body is only available
21
+ // once the account is actually being refused, and is the authoritative one.
22
+ //
23
+ // They are therefore kept as separate windows rather than merged: a merged
24
+ // number would look precise and be neither. The header window is listed first
25
+ // because it is what a caller sees day to day, and the authoritative one is
26
+ // appended beneath it.
27
+
28
+ import type { ProviderQuotaWindow } from "../../quota/quota-contracts";
29
+ import { isoDate, number, percentWindow, record, text } from "../../quota/quota-contracts";
30
+
31
+ /** Header names the CLI plane uses for its token bucket. */
32
+ const HEADER_LIMIT_TOKENS = "x-ratelimit-limit-tokens";
33
+ const HEADER_REMAINING_TOKENS = "x-ratelimit-remaining-tokens";
34
+ const HEADER_REMAINING_REQUESTS = "x-ratelimit-remaining-requests";
35
+ const HEADER_RESET_TOKENS = "x-ratelimit-reset-tokens";
36
+
37
+ /** The upstream code that carries the authoritative free-usage allowance. */
38
+ export const GROK_FREE_USAGE_EXHAUSTED = "subscription:free-usage-exhausted";
39
+
40
+ /** Kinds, so the view can tell an indicator from a verdict. */
41
+ export const GROK_HEADER_WINDOW_KIND = "grok:cli-header";
42
+ export const GROK_HEADER_REQUESTS_WINDOW_KIND = "grok:cli-header-requests";
43
+ export const GROK_AUTHORITATIVE_WINDOW_KIND = "grok:free-usage-authoritative";
44
+
45
+ /** The per-minute request cap the CLI plane applies. */
46
+ const REQUEST_WINDOW_LIMIT = 21;
47
+
48
+ /** One reading of the CLI token bucket. */
49
+ export interface GrokRateLimitReading {
50
+ readonly limitTokens: number | null;
51
+ readonly remainingTokens: number | null;
52
+ readonly remainingRequests: number | null;
53
+ readonly resetsAt: string | null;
54
+ }
55
+
56
+ /** A `Headers`-like reader, narrowed at the boundary rather than asserted. */
57
+ function headerReader(
58
+ headers: { get(name: string): string | null } | Record<string, string>,
59
+ ): (name: string) => string | null {
60
+ if (typeof headers.get === "function") {
61
+ const reader = headers as { get(name: string): string | null };
62
+ return (name) => reader.get(name);
63
+ }
64
+ const literal = headers as Record<string, string>;
65
+ return (name) => literal[name] ?? null;
66
+ }
67
+
68
+ /**
69
+ * Reads the CLI token bucket off a response.
70
+ *
71
+ * `Headers` and a plain object both reach here — the quota probe gets the real
72
+ * thing, a test gets a literal — so both shapes are accepted rather than
73
+ * forcing one caller to adapt.
74
+ */
75
+ export function readGrokRateLimitHeaders(
76
+ headers: { get(name: string): string | null } | Record<string, string>,
77
+ ): GrokRateLimitReading {
78
+ const get = headerReader(headers);
79
+ const resetSeconds = number(get(HEADER_RESET_TOKENS));
80
+ return {
81
+ limitTokens: number(get(HEADER_LIMIT_TOKENS)),
82
+ remainingTokens: number(get(HEADER_REMAINING_TOKENS)),
83
+ remainingRequests: number(get(HEADER_REMAINING_REQUESTS)),
84
+ resetsAt:
85
+ isoDate(get(HEADER_RESET_TOKENS)) ??
86
+ (resetSeconds !== null && resetSeconds > 0
87
+ ? new Date(Date.now() + resetSeconds * 1000).toISOString()
88
+ : null),
89
+ };
90
+ }
91
+
92
+ /**
93
+ * The header bucket as a quota window.
94
+ *
95
+ * Marked independent: this is the CLI request bucket, a different budget from
96
+ * the free-usage allowance, so it must never be summed into a credit total.
97
+ */
98
+ export function grokHeaderWindow(reading: GrokRateLimitReading): ProviderQuotaWindow | null {
99
+ if (reading.remainingTokens === null && reading.limitTokens === null) return null;
100
+ const used =
101
+ reading.remainingTokens !== null && reading.limitTokens !== null
102
+ ? reading.limitTokens - reading.remainingTokens
103
+ : null;
104
+ // The third argument is the *percentage*, not the count. Passing null left
105
+ // `usedPercent` null, and the bar renders a null percentage as grey — so a
106
+ // window that knew its exact used and limit still drew as unknown.
107
+ const usedPercent =
108
+ reading.limitTokens !== null && reading.limitTokens > 0 && used !== null
109
+ ? (used / reading.limitTokens) * 100
110
+ : null;
111
+ return percentWindow(
112
+ GROK_HEADER_WINDOW_KIND,
113
+ "Rate limit token (header)",
114
+ usedPercent,
115
+ reading.resetsAt,
116
+ used,
117
+ reading.limitTokens,
118
+ "independent",
119
+ );
120
+ }
121
+
122
+ /**
123
+ * The request bucket as its own window.
124
+ *
125
+ * Separate from the token bucket on purpose: `x-ratelimit-remaining-requests`
126
+ * is a per-minute cap (21) with no token equivalent, and folding it into the
127
+ * token window produced one number that described neither budget.
128
+ */
129
+ export function grokHeaderRequestsWindow(
130
+ reading: GrokRateLimitReading,
131
+ ): ProviderQuotaWindow | null {
132
+ if (reading.remainingRequests === null) return null;
133
+ const used = REQUEST_WINDOW_LIMIT - reading.remainingRequests;
134
+ const usedPercent = (used / REQUEST_WINDOW_LIMIT) * 100;
135
+ return percentWindow(
136
+ GROK_HEADER_REQUESTS_WINDOW_KIND,
137
+ "Rate limit request (header)",
138
+ usedPercent,
139
+ reading.resetsAt,
140
+ used,
141
+ REQUEST_WINDOW_LIMIT,
142
+ "independent",
143
+ );
144
+ }
145
+
146
+ /** The authoritative allowance a 429 states, parsed out of its message. */
147
+ export interface GrokFreeUsageVerdict {
148
+ readonly actual: number | null;
149
+ readonly limit: number | null;
150
+ readonly resetsAt: string | null;
151
+ readonly rolling: boolean;
152
+ }
153
+
154
+ /**
155
+ * Parses `tokens (actual/limit): 603024/600000` out of a refusal.
156
+ *
157
+ * The numbers are in prose, not in a field, so this is a text match — but the
158
+ * shape is the only place upstream publishes the real allowance, and a refusal
159
+ * that does not carry it yields `null` rather than a guess.
160
+ */
161
+ export function parseGrokFreeUsageVerdict(body: unknown, message?: string): GrokFreeUsageVerdict | null {
162
+ const root = record(body) ?? {};
163
+ const error = text(root["error"] ?? root["message"]) ?? message ?? "";
164
+ const haystack = `${error} ${text(root["code"]) ?? ""}`;
165
+ const match = /tokens\s*\(actual\/limit\)\s*:?\s*([\d_,]+)\s*\/\s*([\d_,]+)/i.exec(haystack);
166
+ const actual = match?.[1] ? number(match[1].replaceAll(/[_,]/g, "")) : null;
167
+ const limit = match?.[2] ? number(match[2].replaceAll(/[_,]/g, "")) : null;
168
+ if (actual === null && limit === null) {
169
+ // A refusal without the figures still tells us the code; it just cannot
170
+ // tell us the allowance.
171
+ if (!haystack.toLowerCase().includes("free-usage") && !haystack.toLowerCase().includes("rolling 24")) return null;
172
+ }
173
+ const rolling = /rolling\s*24[\s-]*hour/i.test(haystack);
174
+ const resetMatch = /resets?\s+(?:over|in|at)\s+([^.;]{0,60})/i.exec(haystack);
175
+ return {
176
+ actual,
177
+ limit,
178
+ resetsAt: resetMatch ? isoDate(resetMatch[1]) ?? null : null,
179
+ rolling,
180
+ };
181
+ }
182
+
183
+ /**
184
+ * The authoritative allowance as a quota window.
185
+ *
186
+ * `remaining` is what decides whether the account can serve, so it is derived
187
+ * rather than left null: an account that has spent 603024 of 600000 has none
188
+ * left, and reporting "unknown" there would leave a refused account looking
189
+ * merely unmeasured.
190
+ */
191
+ export function grokAuthoritativeWindow(verdict: GrokFreeUsageVerdict): ProviderQuotaWindow | null {
192
+ if (verdict.limit === null && verdict.actual === null) return null;
193
+ return percentWindow(
194
+ GROK_AUTHORITATIVE_WINDOW_KIND,
195
+ "Free usage allowance (authoritative)",
196
+ null,
197
+ verdict.resetsAt,
198
+ verdict.actual,
199
+ verdict.limit,
200
+ // Independent: the free allowance is per model per rolling day, not part of
201
+ // a pooled credit balance.
202
+ "independent",
203
+ );
204
+ }
205
+
206
+ /** One probe's two readings: the coarse header and the authoritative refusal. */
207
+ export interface GrokRateLimitProbe {
208
+ /** The CLI token bucket from the response headers. */
209
+ readonly header: ProviderQuotaWindow | null;
210
+ /** The per-minute request bucket, which has no token equivalent. */
211
+ readonly requests: ProviderQuotaWindow | null;
212
+ /** The free-usage allowance from a 429 body, or `null` when not refused. */
213
+ readonly authoritative: ProviderQuotaWindow | null;
214
+ /** Whether upstream refused the probe at all. */
215
+ readonly refusal: boolean;
216
+ /** The refusal code, for the console note. */
217
+ readonly note: string | null;
218
+ }
@@ -373,11 +373,10 @@ function normalizeTools(payload: Record<string, unknown>): void {
373
373
  normalized.push(tool);
374
374
  continue;
375
375
  }
376
- // Tools arrive factory-normalized (flat `{type:"function",name,parameters}`;
377
- // canonical tools are flat, so the legacy nested `function.*` resolution
378
- // duplicated the factory payload path and is deleted). Only grok wire
379
- // strictness stays here: trimmed names, empty-name drop, parameters
380
- // fallback, custom-tool synthesis.
376
+ // Tools arrive factory-normalized (flat `{type:"function",name,parameters}`
377
+ // — canonical tools are flat, so there is no nested `function.*` payload to
378
+ // resolve). Only grok wire strictness stays here: trimmed names,
379
+ // empty-name drop, parameters fallback, custom-tool synthesis.
381
380
  const name = typeof tool.name === "string" ? tool.name : "";
382
381
  if (!name.trim()) continue;
383
382
  const parameters =
@@ -413,8 +412,8 @@ function normalizeTools(payload: Record<string, unknown>): void {
413
412
  if (type === "function" || type === "custom") {
414
413
  // Unlike `tools`, `tool_choice` genuinely arrives nested: the chat
415
414
  // encoder writes `{type:"function", function:{name}}` and
416
- // `{type:"custom", custom:{name}}`, so these reads are not a legacy
417
- // leftover even though the tools loop above dropped its own nested path.
415
+ // `{type:"custom", custom:{name}}`, so these nested reads are required
416
+ // even though the tools loop above has no nested path.
418
417
  const fn = value.function;
419
418
  const nested = fn && typeof fn === "object" && !Array.isArray(fn) ? fn as Record<string, unknown> : undefined;
420
419
  const custom = value.custom;
@@ -28,17 +28,15 @@ const HERMES_PROVIDER_ID = "hermes" as const;
28
28
  * (`agent/portal_tags.py` in `NousResearch/hermes-agent`). Neither is required
29
29
  * for dispatch — a request carrying only `user=` answers 200 — but they are
30
30
  * sent because they are the client's real attribution and cost nothing. The
31
- * client version tracks the released tag `v2026.9.24`, which is date-based:
32
- * Hermes does not publish a semver, so the `0.13.0` this used to claim was not
33
- * a version Hermes ever had.
31
+ * client version is read from `getHermesVersion()` and sent in the shape the
32
+ * released tags use (`v<version>`): Hermes does not publish a semver, so its
33
+ * versions are date-based release tags.
34
34
  */
35
35
  /**
36
36
  * Builds the portal tags for one dispatch.
37
37
  *
38
- * `client=` carries the version in the shape the released tag used
39
- * (`v<version>`), but the version itself is now the resolved one: the previous
40
- * constant named a date tag (`v2026.9.24`) that was neither a release nor the
41
- * version the project publishes. `getHermesVersion()` tracks the real release.
38
+ * `client=` carries the resolved version in the shape the released tags use
39
+ * (`v<version>`); `getHermesVersion()` tracks the real release.
42
40
  */
43
41
  function hermesPortalTags(): readonly string[] {
44
42
  return [
@@ -66,25 +64,23 @@ import type { ModelDefinition } from "../provider-model-definition";
66
64
  *
67
65
  * `loadModelDiscovery` supersedes this list whenever `/v1/models` answers, so
68
66
  * these numbers are only read by a fetch that failed. They are the upstream's
69
- * own `context_length` and `top_provider.max_completion_tokens` — the previous
70
- * values were a uniform `8192` output for every row, which understated every
71
- * one of them.
67
+ * own `context_length` and `top_provider.max_completion_tokens`, stated per row
68
+ * because they differ per row.
72
69
  *
73
- * Two ids that used to live here are gone because upstream no longer carries
74
- * them: `tencent/hy3:free` (absent from the catalog entirely) and
75
- * `upstage/solar-pro4:free` (only the billed `upstage/solar-pro4` and the free
76
- * `upstage/solar-mini4:free` exist).
70
+ * Two ids upstream no longer carries are absent: `tencent/hy3:free` (missing
71
+ * from the catalog entirely) and `upstage/solar-pro4:free` (only the billed
72
+ * `upstage/solar-pro4` and the free `upstage/solar-mini4:free` exist).
77
73
  */
78
74
  export const HERMES_MODELS: readonly ModelDefinition[] = [
79
75
  // The live `/models` free rows, with the limits the catalog states. Verified
80
76
  // against the API: every id here answers 200.
81
77
  //
82
- // `meituan/longcat-2.0:free` is gone: the catalog still lists the id, but
83
- // dispatching it answers 404 "This model is no longer free", so keeping it
78
+ // `meituan/longcat-2.0:free` is excluded: the catalog still lists the id, but
79
+ // dispatching it answers 404 "This model is no longer free", so listing it
84
80
  // would be a card that can only ever fail. `meituan/longcat-2.5-preview:free`
85
- // replaces it and answers 200.
81
+ // is listed instead and answers 200.
86
82
  //
87
- // `stepfun/step-5-preview:free` is gone: it is absent from the live catalog
83
+ // `stepfun/step-5-preview:free` is excluded: it is absent from the live catalog
88
84
  // entirely and dispatching it answers 404 "This model's free period has
89
85
  // ended" — a retired row, not a capacity blip, so it cannot be left in.
90
86
  //
@@ -31,7 +31,7 @@ const MAX_DISCOVERED_MODELS = 500;
31
31
  const KIRO_EFFORTS = ["low", "medium", "high", "xhigh"] as const;
32
32
 
33
33
  /**
34
- * Ids the seed catalog deliberately no longer carries.
34
+ * Ids the seed catalog deliberately excludes.
35
35
  *
36
36
  * Discovery adds every id the upstream lists, so without this a retired row
37
37
  * comes back the moment an account fetches its models. The seed is the owner
@@ -8,7 +8,7 @@
8
8
  // Two upstream behaviours drive the header and endpoint logic:
9
9
  //
10
10
  // * There is exactly one generation surface, `q.{region}.amazonaws.com`. The
11
- // legacy `codewhisperer.*` host and the vendor's own path-style gateway are
11
+ // `codewhisperer.*` host and the vendor's own path-style gateway are
12
12
  // not what the real client talks to, and reaching for them is actively
13
13
  // harmful: an account that has just been refused would immediately probe two
14
14
  // other service entries with the same token, which is the shape of credential
@@ -6,12 +6,12 @@ const MISTRAL_PROVIDER_ID = "mistral" as const;
6
6
  /**
7
7
  * Mistral's own current generalist tiers.
8
8
  *
9
- * The provider is registered as a generic OpenAI-compatible API-key provider, so
10
- * it carried no catalog at all and every model it serves was absent until a
11
- * discovery run happened to fetch them. These are the two `latest` aliases
12
- * Mistral keeps pointed at its current generalist models — `mistral-large-latest`
13
- * (Large 4) and `mistral-medium-latest` (Medium 3.5), per the Mistral model
14
- * overview.
9
+ * The provider is registered as a generic OpenAI-compatible API-key provider,
10
+ * which carries no catalog of its own: without this list, every model it serves
11
+ * stays absent until a discovery run happens to fetch them. These are the two
12
+ * `latest` aliases Mistral keeps pointed at its current generalist models —
13
+ * `mistral-large-latest` (Large 4) and `mistral-medium-latest` (Medium 3.5),
14
+ * per the Mistral model overview.
15
15
  *
16
16
  * Limits and pricing come from the base catalog via `providerId`: `defineModel`
17
17
  * resolves `mistral:<id>` and falls back to the majority row, so no number is
@@ -23,11 +23,11 @@ const SESSION_COUNTER_MAX = 63;
23
23
  * `ses_` + 12-char hex (bitwise inverted millisecond timestamp * 4096 + seq) + 14-char base62.
24
24
  *
25
25
  * The counter is capped rather than left to grow. Sixty-four dispatches inside
26
- * one millisecond used to emit ids the gate refuses, so a burst — exactly what
27
- * a proxy pool produces — 403'd on its own headers while every other field was
26
+ * one millisecond emit ids the gate refuses, so a burst — exactly what a proxy
27
+ * pool produces — would 403 on its own headers while every other field was
28
28
  * correct. On overflow the timestamp advances by one millisecond instead: the
29
29
  * id stays inside the window, and the only cost is that ids minted in the same
30
- * real millisecond no longer sort as the client's would.
30
+ * real millisecond do not sort as the client's would.
31
31
  */
32
32
  export function generateOpenCodeSessionId(now = Date.now()): string {
33
33
  if (now !== lastTimestamp) {
@@ -79,7 +79,7 @@ const UNAVAILABLE_FREE_ZEN_IDS: readonly string[] = [
79
79
  * `CARTETHYIA_OPENCODE_UNAVAILABLE_FREE` lets an operator correct that
80
80
  * immediately: a comma-separated list that *replaces* the bundled one, or the
81
81
  * single token `none` to trust the listing completely. The bundled ids remain
82
- * the default so an unconfigured install behaves exactly as it does today.
82
+ * the default so an unconfigured install keeps these refusals.
83
83
  */
84
84
  function unavailableFreeZenIds(): readonly string[] {
85
85
  const override = resolveOpenCodeUnavailableFree();
@@ -116,8 +116,12 @@ export function isSystemoneZenModel(modelId: string): boolean {
116
116
  * fail a probe.
117
117
  */
118
118
  export function isFreeTierZenModel(modelId: string): boolean {
119
- if (unavailableFreeZenIds().includes(modelId)) return false;
120
- return modelId.endsWith("-free") || KNOWN_FREE_ZEN_IDS.includes(modelId);
119
+ // Dispatch ids arrive provider-prefixed (`opencode/big-pickle`) while the
120
+ // catalog and `KNOWN_FREE_ZEN_IDS` hold the bare id, so match on the bare
121
+ // id rather than the prefixed one.
122
+ const bare = modelId.slice(modelId.indexOf("/") + 1);
123
+ if (unavailableFreeZenIds().includes(bare)) return false;
124
+ return bare.endsWith("-free") || KNOWN_FREE_ZEN_IDS.includes(bare);
121
125
  }
122
126
 
123
127
  /**
@@ -159,11 +163,11 @@ function opencodeDesktopHeaders(
159
163
  // The free-tier gate refuses non-streaming dispatches with 403
160
164
  // FreeTierError, so free models always advertise SSE regardless of the
161
165
  // client's own Accept; billed models keep the JSON default. The value is
162
- // what OpenCode 2.x's AI SDK sends, not the narrower
163
- // `text/event-stream` we used to claim — the gate always accepted the
164
- // narrow one, so this is alignment with the real client rather than a
165
- // fix for a live failure. It costs nothing: `stream: true` is forced on
166
- // the body either way, so the upstream answers SSE regardless.
166
+ // what OpenCode 2.x's AI SDK sends — a broader Accept than the narrower
167
+ // `text/event-stream` the gate is known to accept, so this is alignment
168
+ // with the real client rather than a fix for a live failure. It costs
169
+ // nothing: `stream: true` is forced on the body either way, so the upstream
170
+ // answers SSE regardless.
167
171
  ...(freeModel ? { accept: FREE_TIER_ACCEPT } : {}),
168
172
  };
169
173
  }
@@ -493,29 +497,24 @@ export function systemoneLimits(): Pick<ModelDefinition, "contextLimit" | "outpu
493
497
 
494
498
  export const OPENCODE_FREE_MODELS: readonly ModelDefinition[] = [
495
499
  ...sharedZenChat,
496
- // Free-tier chat models the shared Zen listing advertises with the `-free`
497
- // convention. Each was dispatched through this adapter (its own fingerprint
498
- // headers, agent tools) and answered, so the row is routable, not merely
499
- // listed. Limits come from the models.dev snapshot under `opencode`; a row
500
- // the snapshot does not file keeps an explicit limit rather than the generic
501
- // default.
502
- defineModel({ id: "nemotron-3-ultra-free", providerId: "opencode", wireFamily: "chat", endpoint: `${ZEN_PATH_PREFIX}/chat/completions`, reasoning: true, free: true }),
503
- defineModel({ id: "nemotron-3.5-lightning-free", providerId: "opencode", wireFamily: "chat", endpoint: `${ZEN_PATH_PREFIX}/chat/completions`, reasoning: true, free: true }),
504
- // Absent from the snapshot: keep declared limits rather than the generic default.
505
- defineModel({ id: "longcat-2.5-preview-free", providerId: "opencode", wireFamily: "chat", endpoint: `${ZEN_PATH_PREFIX}/chat/completions`, ctx: 1_000_000, out: 131_072, reasoning: true, free: true }),
500
+ // `nemotron-3-ultra-free`, `nemotron-3.5-lightning-free` and
501
+ // `longcat-2.5-preview-free` are excluded: the live Zen roster does not list
502
+ // them. A row the provider's own roster dropped can disappear from the wire
503
+ // without notice, which makes it a stale row — one that routes to a 404 at
504
+ // dispatch time rather than failing at catalog load. `big-pickle` and the
505
+ // rest of the shared Zen listing remain.
506
506
  systemoneZen("jev-1.13-free", true),
507
507
  ];
508
508
 
509
509
  export const OPENCODE_ZEN_MODELS: readonly ModelDefinition[] = [
510
510
  ...sharedZenChat,
511
- defineModel({ id: "nemotron-3-ultra-free", providerId: "opencode", wireFamily: "chat", endpoint: `${ZEN_PATH_PREFIX}/chat/completions`, reasoning: true }),
512
511
  defineModel({ id: "muse-spark-1.2", providerId: "opencode", wireFamily: "responses", endpoint: `${ZEN_PATH_PREFIX}/responses`, vision: true, reasoning: true, reasoningEfforts: MUSE_SPARK_REASONING_EFFORTS }),
513
512
  defineModel({ id: "muse-spark-1.3", providerId: "opencode", wireFamily: "responses", endpoint: `${ZEN_PATH_PREFIX}/responses`, vision: true, reasoning: true, reasoningEfforts: MUSE_SPARK_REASONING_EFFORTS }),
514
513
  systemoneZen("jev-1.13", false),
515
514
  ];
516
515
 
517
516
  export const OPENCODE_GO_MODELS: readonly ModelDefinition[] = [
518
- // `muse-spark-1.2` is retired upstream; the catalog files
517
+ // `muse-spark-1.2` is not served upstream; the catalog files
519
518
  // `muse-spark-1.2-contributor` at 1048576/131072, which is what this row
520
519
  // serves.
521
520
  defineModel({ id: "muse-spark-1.2-contributor", providerId: "opencode-go", wireFamily: "responses", endpoint: `${GO_PATH_PREFIX}/responses`, ctx: 1048576, out: 131072, vision: true, reasoning: true }),