cartethyia 2.7.4 → 2.7.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 (595) hide show
  1. package/.env.example +5 -0
  2. package/README.md +18 -6
  3. package/dist/dashboard/assets/BannedPage-B1N603cM.js +1 -0
  4. package/dist/dashboard/assets/BannedPage-B1N603cM.js.gz +0 -0
  5. package/dist/dashboard/assets/CliToolDetailPage-9q4_4NoK.js +3 -0
  6. package/dist/dashboard/assets/CliToolDetailPage-9q4_4NoK.js.gz +0 -0
  7. package/dist/dashboard/assets/CliToolsPage-DwshGtK-.js +1 -0
  8. package/dist/dashboard/assets/CliToolsPage-DwshGtK-.js.gz +0 -0
  9. package/dist/dashboard/assets/CombosPage-D762MIkp.js +1 -0
  10. package/dist/dashboard/assets/CombosPage-D762MIkp.js.gz +0 -0
  11. package/dist/dashboard/assets/ConsoleLogPage-KRYjHZsX.js +1 -0
  12. package/dist/dashboard/assets/ConsoleLogPage-KRYjHZsX.js.gz +0 -0
  13. package/dist/dashboard/assets/CustomizationPage-DXsxk5uM.js +1 -0
  14. package/dist/dashboard/assets/CustomizationPage-DXsxk5uM.js.gz +0 -0
  15. package/dist/dashboard/assets/HealthEventsModal-BEjAjD4F.js +1 -0
  16. package/dist/dashboard/assets/HealthEventsModal-BEjAjD4F.js.gz +0 -0
  17. package/dist/dashboard/assets/InjectSystemPromptPanel-d5v3K_Yc.js +899 -0
  18. package/dist/dashboard/assets/InjectSystemPromptPanel-d5v3K_Yc.js.gz +0 -0
  19. package/dist/dashboard/assets/LoginPage-Ccv4tjNS.js +1 -0
  20. package/dist/dashboard/assets/LoginPage-Ccv4tjNS.js.gz +0 -0
  21. package/dist/dashboard/assets/{ModelPicker-BMS2t8BA.js → ModelPicker-C6WlpU5E.js} +1 -1
  22. package/dist/dashboard/assets/ModelPicker-C6WlpU5E.js.gz +0 -0
  23. package/dist/dashboard/assets/OverviewPage-DN9fYNWA.js +1 -0
  24. package/dist/dashboard/assets/OverviewPage-DN9fYNWA.js.gz +0 -0
  25. package/dist/dashboard/assets/ProviderDetailPage-BY0ieUgz.js +5 -0
  26. package/dist/dashboard/assets/ProviderDetailPage-BY0ieUgz.js.gz +0 -0
  27. package/dist/dashboard/assets/ProviderIcon-jjqXJDA3.js +1 -0
  28. package/dist/dashboard/assets/ProviderIcon-jjqXJDA3.js.gz +0 -0
  29. package/dist/dashboard/assets/ProvidersPage-DNsbfea2.js +1 -0
  30. package/dist/dashboard/assets/ProvidersPage-DNsbfea2.js.gz +0 -0
  31. package/dist/dashboard/assets/{ProxyPage-lsrx7NYg.js → ProxyPage-DxjvUzSD.js} +1 -1
  32. package/dist/dashboard/assets/ProxyPage-DxjvUzSD.js.gz +0 -0
  33. package/dist/dashboard/assets/QuotaPage-OaHScjpX.js +1 -0
  34. package/dist/dashboard/assets/QuotaPage-OaHScjpX.js.gz +0 -0
  35. package/dist/dashboard/assets/SettingsPage-BW4AKj2H.js +1 -0
  36. package/dist/dashboard/assets/SettingsPage-BW4AKj2H.js.gz +0 -0
  37. package/dist/dashboard/assets/SetupPage-44BZrxFC.js +1 -0
  38. package/dist/dashboard/assets/SetupPage-44BZrxFC.js.gz +0 -0
  39. package/dist/dashboard/assets/{StudioPage-Cq5tW7hW.js → StudioPage-DRPavCdU.js} +4 -4
  40. package/dist/dashboard/assets/StudioPage-DRPavCdU.js.gz +0 -0
  41. package/dist/dashboard/assets/ToolIcon-CfeLA-Z_.js +1 -0
  42. package/dist/dashboard/assets/ToolIcon-CfeLA-Z_.js.gz +0 -0
  43. package/dist/dashboard/assets/UsagePage-B7M7LvMM.js +56 -0
  44. package/dist/dashboard/assets/UsagePage-B7M7LvMM.js.gz +0 -0
  45. package/dist/dashboard/assets/api-keys-KvX83mRe.js +1 -0
  46. package/dist/dashboard/assets/api-keys-KvX83mRe.js.gz +0 -0
  47. package/dist/dashboard/assets/app-DRnQLhsc.js +9 -0
  48. package/dist/dashboard/assets/app-DRnQLhsc.js.gz +0 -0
  49. package/dist/dashboard/assets/app-mweRcsKp.css +2 -0
  50. package/dist/dashboard/assets/app-mweRcsKp.css.gz +0 -0
  51. package/dist/dashboard/assets/assert-CidQVoNo.js +1 -0
  52. package/dist/dashboard/assets/assert-CidQVoNo.js.gz +0 -0
  53. package/dist/dashboard/assets/{badge-D9CS6dZk.js → badge-BaX2GTrn.js} +1 -1
  54. package/dist/dashboard/assets/badge-BaX2GTrn.js.gz +0 -0
  55. package/dist/dashboard/assets/{banner-DympBNq4.js → banner-DFH4Bkte.js} +1 -1
  56. package/dist/dashboard/assets/banner-DFH4Bkte.js.gz +0 -0
  57. package/dist/dashboard/assets/brand/login_register.jpg +0 -0
  58. package/dist/dashboard/assets/brand/login_register_initial.jpg +0 -0
  59. package/dist/dashboard/assets/brand/og_bansos.jpg +0 -0
  60. package/dist/dashboard/assets/brand/og_image.jpg +0 -0
  61. package/dist/dashboard/assets/{card-ibRWU_uT.js → card-CxDw0lK6.js} +1 -1
  62. package/dist/dashboard/assets/card-CxDw0lK6.js.gz +0 -0
  63. package/dist/dashboard/assets/chevron-left-Bf6CaTSJ.js +1 -0
  64. package/dist/dashboard/assets/chevron-left-Bf6CaTSJ.js.gz +0 -0
  65. package/dist/dashboard/assets/circle-question-mark-Cz4un8sH.js +1 -0
  66. package/dist/dashboard/assets/circle-question-mark-Cz4un8sH.js.gz +0 -0
  67. package/dist/dashboard/assets/cli-tools-Bwv5-zGZ.js +1 -0
  68. package/dist/dashboard/assets/cli-tools-Bwv5-zGZ.js.gz +0 -0
  69. package/dist/dashboard/assets/{clipboard-button-DJm-9pz7.js → clipboard-button-DfS1ntiq.js} +1 -1
  70. package/dist/dashboard/assets/clipboard-button-DfS1ntiq.js.gz +0 -0
  71. package/dist/dashboard/assets/dialog-BK-NRr5u.js +1 -0
  72. package/dist/dashboard/assets/dialog-BK-NRr5u.js.gz +0 -0
  73. package/dist/dashboard/assets/{drawer-CqGYeOrd.js → drawer-QqPul6sC.js} +1 -1
  74. package/dist/dashboard/assets/drawer-QqPul6sC.js.gz +0 -0
  75. package/dist/dashboard/assets/entry-BMlYJVFj.js +1 -0
  76. package/dist/dashboard/assets/entry-BMlYJVFj.js.gz +0 -0
  77. package/dist/dashboard/assets/entry-D6WvojeD.js +4 -0
  78. package/dist/dashboard/assets/entry-D6WvojeD.js.gz +0 -0
  79. package/dist/dashboard/assets/entry-Dmx7gBiN.js +1 -0
  80. package/dist/dashboard/assets/entry-Dmx7gBiN.js.gz +0 -0
  81. package/dist/dashboard/assets/{formatters-D-MTJxMr.js → formatters-u7JPB5BX.js} +1 -1
  82. package/dist/dashboard/assets/formatters-u7JPB5BX.js.gz +0 -0
  83. package/dist/dashboard/assets/house-h9AZlJuT.js +1 -0
  84. package/dist/dashboard/assets/house-h9AZlJuT.js.gz +0 -0
  85. package/dist/dashboard/assets/{index-DBNPOfhV.js → index-Ko4dLRnV.js} +2 -2
  86. package/dist/dashboard/assets/index-Ko4dLRnV.js.gz +0 -0
  87. package/dist/dashboard/assets/{inline-BT0zntEr.js → inline-CHJQwxLa.js} +1 -1
  88. package/dist/dashboard/assets/inline-CHJQwxLa.js.gz +0 -0
  89. package/dist/dashboard/assets/{input-Da2ZViWg.js → input-BlmlizVV.js} +1 -1
  90. package/dist/dashboard/assets/input-BlmlizVV.js.gz +0 -0
  91. package/dist/dashboard/assets/{layout-BXNeLw6M.js → layout-Bz93I-yE.js} +1 -1
  92. package/dist/dashboard/assets/layout-Bz93I-yE.js.gz +0 -0
  93. package/dist/dashboard/assets/{list-row-BycOrrIV.js → list-row-Ml16KCfu.js} +1 -1
  94. package/dist/dashboard/assets/list-row-Ml16KCfu.js.gz +0 -0
  95. package/dist/dashboard/assets/live-7_QpnYGu.js +1 -0
  96. package/dist/dashboard/assets/live-7_QpnYGu.js.gz +0 -0
  97. package/dist/dashboard/assets/{mutation-Rr0TIvcj.js → mutation-GU7XdEjz.js} +1 -1
  98. package/dist/dashboard/assets/mutation-GU7XdEjz.js.gz +0 -0
  99. package/dist/dashboard/assets/network-CuMDaTzT.js +1 -0
  100. package/dist/dashboard/assets/network-CuMDaTzT.js.gz +0 -0
  101. package/dist/dashboard/assets/numbers-CoqpaDMx.js +2 -0
  102. package/dist/dashboard/assets/numbers-CoqpaDMx.js.gz +0 -0
  103. package/dist/dashboard/assets/pagination-B5aOoweZ.js +1 -0
  104. package/dist/dashboard/assets/pagination-B5aOoweZ.js.gz +0 -0
  105. package/dist/dashboard/assets/providers/zai.webp +0 -0
  106. package/dist/dashboard/assets/providers/zcode.webp +0 -0
  107. package/dist/dashboard/assets/{query-keys-BloKtaXJ.js → query-keys-YSoQOUh6.js} +1 -1
  108. package/dist/dashboard/assets/{query-keys-BloKtaXJ.js.gz → query-keys-YSoQOUh6.js.gz} +0 -0
  109. package/dist/dashboard/assets/quota-DJ1QlAOq.js +1 -0
  110. package/dist/dashboard/assets/quota-DJ1QlAOq.js.gz +0 -0
  111. package/dist/dashboard/assets/routing-fnCsxgNO.js +1 -0
  112. package/dist/dashboard/assets/routing-fnCsxgNO.js.gz +0 -0
  113. package/dist/dashboard/assets/{select-BIYIBkMU.js → select-BHqpd-bg.js} +2 -2
  114. package/dist/dashboard/assets/select-BHqpd-bg.js.gz +0 -0
  115. package/dist/dashboard/assets/settings-BxslsErn.js +1 -0
  116. package/dist/dashboard/assets/settings-BxslsErn.js.gz +0 -0
  117. package/dist/dashboard/assets/shield-BMQNxORh.js +1 -0
  118. package/dist/dashboard/assets/shield-BMQNxORh.js.gz +0 -0
  119. package/dist/dashboard/assets/{stack-DfgnFDWY.js → stack-j7cXjgYX.js} +1 -1
  120. package/dist/dashboard/assets/stack-j7cXjgYX.js.gz +0 -0
  121. package/dist/dashboard/assets/{state-CkfdYWel.js → state-DHmEnLa2.js} +1 -1
  122. package/dist/dashboard/assets/state-DHmEnLa2.js.gz +0 -0
  123. package/dist/dashboard/assets/{switch-SYm237HG.js → switch-DmZcyTIC.js} +1 -1
  124. package/dist/dashboard/assets/switch-DmZcyTIC.js.gz +0 -0
  125. package/dist/dashboard/assets/{useMutation-BQqCRNIi.js → useMutation-CbCP4w1o.js} +1 -1
  126. package/dist/dashboard/assets/useMutation-CbCP4w1o.js.gz +0 -0
  127. package/dist/dashboard/console-backgrounds.jpg +0 -0
  128. package/dist/dashboard/console-backgrounds_initial.jpg +0 -0
  129. package/dist/dashboard/index.html +2 -2
  130. package/dist/dashboard/index.html.gz +0 -0
  131. package/dist/dashboard/precompressed-assets.json +60 -59
  132. package/migrations/full-postgres/0036_system_prompt_presets_refs.sql +10 -0
  133. package/migrations/full-postgres/0037_share_concurrency_scope.sql +3 -0
  134. package/migrations/full-postgres/0038_add_accounts_quota_snapshot.sql +7 -0
  135. package/migrations/full-postgres/0039_add_telemetry_provider_model_indexes.sql +13 -0
  136. package/migrations/full-postgres/0040_add_models_dialect.sql +3 -0
  137. package/migrations/full-postgres/0041_add_daily_checkin_ledger_account_fk.sql +7 -0
  138. package/migrations/full-postgres/0042_add_telemetry_degenerate_stop.sql +14 -0
  139. package/migrations/lite-sqlite3/0036_system_prompt_presets_refs.sql +14 -0
  140. package/migrations/lite-sqlite3/0037_share_concurrency_scope.sql +3 -0
  141. package/migrations/lite-sqlite3/0038_add_accounts_quota_snapshot.sql +4 -0
  142. package/migrations/lite-sqlite3/0039_add_telemetry_provider_model_indexes.sql +6 -0
  143. package/migrations/lite-sqlite3/0040_add_models_dialect.sql +3 -0
  144. package/migrations/lite-sqlite3/0041_add_daily_checkin_ledger_account_fk.sql +26 -0
  145. package/migrations/lite-sqlite3/0042_add_telemetry_degenerate_stop.sql +12 -0
  146. package/package.json +4 -6
  147. package/scripts/cli/update.ts +15 -41
  148. package/src/boot/app.ts +58 -2
  149. package/src/boot/dependencies.ts +794 -732
  150. package/src/boot/lifecycle.ts +11 -12
  151. package/src/config/cooldowns.ts +34 -55
  152. package/src/config/network.ts +1 -1
  153. package/src/config/package-paths.ts +1 -1
  154. package/src/config/providers.ts +19 -14
  155. package/src/config/resources.ts +0 -5
  156. package/src/config/runtime.ts +2 -5
  157. package/src/config/spec.ts +33 -6
  158. package/src/config/usage.ts +27 -0
  159. package/src/console/api-keys/ban-constants.ts +38 -0
  160. package/src/console/api-keys/contracts.ts +71 -62
  161. package/src/console/api-keys/routes.ts +56 -24
  162. package/src/console/auth/access.ts +4 -4
  163. package/src/console/auth/account-reset.ts +1 -1
  164. package/src/console/auth/audit-recorder.ts +1 -1
  165. package/src/console/auth/lockout.ts +7 -3
  166. package/src/console/auth/row-coercion.ts +57 -12
  167. package/src/console/auth/session-resolver.ts +1 -1
  168. package/src/console/auth/session-service.ts +8 -2
  169. package/src/console/auth/session.ts +37 -31
  170. package/src/console/auth/setup-service.ts +4 -3
  171. package/src/console/auth/user-store.ts +1 -1
  172. package/src/console/backup/9router-map.ts +0 -3
  173. package/src/console/backup/contracts.ts +9 -9
  174. package/src/console/backup/export-endpoint.ts +5 -6
  175. package/src/console/backup/nine-router.ts +1 -7
  176. package/src/console/backup/routes.ts +29 -33
  177. package/src/console/backup/service.ts +468 -468
  178. package/src/console/backup/store.ts +47 -29
  179. package/src/console/backup/validate.ts +11 -1
  180. package/src/console/cli-tools/contracts.ts +43 -299
  181. package/src/console/cli-tools/fs-ops.ts +29 -0
  182. package/src/console/cli-tools/injectors/claude.ts +3 -3
  183. package/src/console/cli-tools/injectors/codex.ts +181 -181
  184. package/src/console/cli-tools/injectors/driver.ts +150 -247
  185. package/src/console/cli-tools/injectors/kilo.ts +146 -21
  186. package/src/console/cli-tools/injectors/opencode.ts +176 -153
  187. package/src/console/cli-tools/routes.ts +7 -1
  188. package/src/console/cli-tools/service.ts +8 -7
  189. package/src/console/cli-tools/store.ts +2 -2
  190. package/src/console/console-router.ts +32 -0
  191. package/src/console/dashboard-assets.ts +9 -9
  192. package/src/console/domain-registration.ts +16 -2
  193. package/src/console/inferhub/client.ts +0 -7
  194. package/src/console/inferhub/key-store.ts +1 -1
  195. package/src/console/inferhub/normalize.ts +2 -2
  196. package/src/console/inferhub/routes.ts +1 -1
  197. package/src/console/inferhub/types.ts +1 -1
  198. package/src/console/observability/contracts.ts +7 -1
  199. package/src/console/observability/live.ts +31 -18
  200. package/src/console/observability/logs.ts +1 -1
  201. package/src/console/observability/sse.ts +62 -8
  202. package/src/console/observability/store.ts +23 -4
  203. package/src/console/providers/catalog/catalog-projections.ts +1 -1
  204. package/src/console/providers/catalog/contracts.ts +39 -0
  205. package/src/console/providers/catalog/model-operations.ts +1 -1
  206. package/src/console/providers/catalog/provider-operations.ts +95 -17
  207. package/src/console/providers/catalog/public-model-store.ts +0 -0
  208. package/src/console/providers/catalog/routes.ts +27 -3
  209. package/src/console/providers/catalog/store.ts +147 -8
  210. package/src/console/providers/detail/routes.ts +1 -1
  211. package/src/console/providers/oauth/callback-listener.ts +100 -18
  212. package/src/console/providers/oauth/routes.ts +92 -13
  213. package/src/console/quota/account-quota-global.ts +41 -3
  214. package/src/console/quota/account-quota-shared.ts +10 -3
  215. package/src/console/quota/account-quota-tenant.ts +259 -66
  216. package/src/console/quota/daily-growth-job.ts +308 -0
  217. package/src/console/quota/native-identity.ts +2 -2
  218. package/src/console/quota/quota-view.ts +1 -1
  219. package/src/console/remote-support/routes.ts +1 -1
  220. package/src/console/remote-support/service.ts +2 -2
  221. package/src/console/routing/model/contracts.ts +1 -1
  222. package/src/console/routing/pools/contracts.ts +2 -2
  223. package/src/console/routing/pools/import-parse.ts +3 -3
  224. package/src/console/routing/pools/relay-deploy.ts +3 -3
  225. package/src/console/settings/contracts.ts +29 -2
  226. package/src/console/settings/store.ts +20 -0
  227. package/src/console/share/share-router.ts +100 -32
  228. package/src/console/share/share-stats.ts +11 -13
  229. package/src/console/share/share-usage.ts +1 -1
  230. package/src/console/shared/errors.ts +2 -2
  231. package/src/console/shared/object.ts +18 -0
  232. package/src/console/shared/read-through-cache.ts +4 -4
  233. package/src/console/shared/transaction.ts +14 -0
  234. package/src/console/system-prompt/contracts.ts +89 -12
  235. package/src/console/system-prompt/routes.ts +98 -45
  236. package/src/console/updates/apply-update.ts +244 -0
  237. package/src/console/updates/routes.ts +252 -97
  238. package/src/main.ts +11 -5
  239. package/src/network/http2-bidi.ts +1 -1
  240. package/src/network/ip-utils.ts +298 -0
  241. package/src/network/outbound-fetch.ts +1 -1
  242. package/src/network/pool/agent.ts +7 -6
  243. package/src/network/pool/byte-accounting.ts +1 -1
  244. package/src/network/pool/resolver.ts +16 -12
  245. package/src/network/pool/selector.ts +150 -136
  246. package/src/network/pool-health-machine.ts +1 -1
  247. package/src/network/rate-limit-verdict.ts +1 -1
  248. package/src/network/retry.ts +1 -1
  249. package/src/network/ssrf.ts +20 -71
  250. package/src/observability/event-log.ts +9 -2
  251. package/src/observability/event-query.ts +61 -291
  252. package/src/observability/log-sanitize.ts +1 -1
  253. package/src/observability/metrics.ts +4 -4
  254. package/src/observability/payload-capture.ts +6 -5
  255. package/src/observability/payload-store.ts +1 -1
  256. package/src/observability/runtime-metrics.ts +1 -1
  257. package/src/observability/telemetry-buffer.ts +19 -3
  258. package/src/observability/telemetry-reconciliation.ts +3 -3
  259. package/src/observability/token-speed.ts +1 -1
  260. package/src/persistence/connection-url.ts +1 -1
  261. package/src/persistence/db-handle.ts +1 -1
  262. package/src/persistence/db-pg.ts +2 -2
  263. package/src/persistence/db-sqlite.ts +3 -3
  264. package/src/persistence/dialect.ts +5 -1
  265. package/src/persistence/readiness.ts +1 -1
  266. package/src/persistence/redis.ts +1 -1
  267. package/src/persistence/schema.ts +121 -40
  268. package/src/persistence/shared-cache.ts +2 -2
  269. package/src/persistence/sort-order.ts +1 -1
  270. package/src/persistence/stores/api-key-store.ts +29 -2
  271. package/src/persistence/stores/share-store.ts +20 -8
  272. package/src/persistence/system-prompt-store.ts +63 -18
  273. package/src/persistence/tenant-preferences.ts +1 -1
  274. package/src/persistence/ttl-cache.ts +3 -3
  275. package/src/persistence/usage-flush.ts +7 -2
  276. package/src/protocol/canonical-model.ts +1 -1
  277. package/src/protocol/codecs.ts +6 -10
  278. package/src/protocol/dsml.ts +8 -125
  279. package/src/protocol/image-clamp.ts +2 -2
  280. package/src/protocol/reasoning-text.ts +1 -1
  281. package/src/protocol/reasoning.ts +7 -52
  282. package/src/protocol/registry.ts +50 -15
  283. package/src/protocol/request/chat.ts +46 -19
  284. package/src/protocol/request/codex.ts +1 -1
  285. package/src/protocol/request/messages.ts +46 -14
  286. package/src/protocol/request/responses.ts +19 -10
  287. package/src/protocol/response/chat.ts +25 -10
  288. package/src/protocol/response/codex.ts +1 -1
  289. package/src/protocol/response/messages.ts +1 -1
  290. package/src/protocol/response/omp-tool-call-markup.ts +1 -1
  291. package/src/protocol/response/responses.ts +1 -1
  292. package/src/protocol/sse-decoder.ts +50 -1
  293. package/src/protocol/stop-reason.ts +2 -2
  294. package/src/protocol/think-split.ts +347 -0
  295. package/src/protocol/translation/attachment-integrity.ts +3 -3
  296. package/src/protocol/translation/cache-controls.ts +8 -20
  297. package/src/protocol/translation/capabilities.ts +6 -6
  298. package/src/protocol/translation/dialect/deepseek.ts +10 -0
  299. package/src/protocol/translation/dialect/inband.ts +39 -0
  300. package/src/protocol/translation/dialect/model-thinking.ts +89 -0
  301. package/src/protocol/translation/dialect/qwen.ts +114 -0
  302. package/src/protocol/translation/dialect/resolver.ts +83 -0
  303. package/src/protocol/translation/dialect/rules.ts +93 -0
  304. package/src/protocol/translation/dialect/thinking-ladder.ts +245 -0
  305. package/src/protocol/translation/dialect/types.ts +58 -0
  306. package/src/protocol/translation/quirks.ts +18 -4
  307. package/src/protocol/translation/thinking.ts +12 -252
  308. package/src/protocol/translation/tool-id.ts +3 -3
  309. package/src/protocol/translation/tool-repair.ts +1 -1
  310. package/src/protocol/upstream/post-json.ts +6 -1
  311. package/src/providers/adapters/compatible-adapter.ts +71 -4
  312. package/src/providers/authentication/jwt-validator.ts +0 -5
  313. package/src/providers/authentication/oauth-client.ts +254 -253
  314. package/src/providers/authentication/oauth-flow-store.ts +42 -7
  315. package/src/providers/bundled-discovery-providers.ts +6 -30
  316. package/src/providers/configured-openai-providers.ts +82 -97
  317. package/src/providers/default-registry.ts +35 -37
  318. package/src/providers/discovery/catalog/catalog-format.ts +13 -2
  319. package/src/providers/discovery/discovery-types.ts +0 -1
  320. package/src/providers/discovery/openai-model-discovery.ts +2 -2
  321. package/src/providers/discovery/probe-phases.ts +48 -13
  322. package/src/providers/discovery/probing-service.ts +6 -8
  323. package/src/providers/integrations/agentrouter/agentrouter.ts +12 -2
  324. package/src/providers/integrations/antigravity/antigravity-oauth.ts +0 -1
  325. package/src/providers/integrations/antigravity/antigravity-protocol.ts +88 -28
  326. package/src/providers/integrations/antigravity/antigravity-quota.ts +18 -6
  327. package/src/providers/integrations/antigravity/antigravity.ts +22 -13
  328. package/src/providers/integrations/buddy/buddy-catalog-shared.ts +1 -1
  329. package/src/providers/integrations/buddy/buddy-chat-shared.ts +20 -4
  330. package/src/providers/integrations/buddy/buddy-cli-headers.ts +19 -52
  331. package/src/providers/integrations/buddy/buddy-identity.ts +86 -0
  332. package/src/providers/integrations/buddy/buddy-oauth-shared.ts +1 -1
  333. package/src/providers/integrations/buddy/codebuddy/cn-provider.ts +1 -2
  334. package/src/providers/integrations/buddy/codebuddy/provider.ts +4 -15
  335. package/src/providers/integrations/buddy/codebuddy/shared.ts +30 -9
  336. package/src/providers/integrations/buddy/workbuddy/provider.ts +5 -9
  337. package/src/providers/integrations/buddy/workbuddy/shared.ts +21 -3
  338. package/src/providers/integrations/claude/claude-cch.ts +148 -148
  339. package/src/providers/integrations/claude/fallback-credit.ts +3 -7
  340. package/src/providers/integrations/cline/cline.ts +1 -1
  341. package/src/providers/integrations/codex/codex-device-code.ts +101 -101
  342. package/src/providers/integrations/codex/codex-oauth.ts +0 -1
  343. package/src/providers/integrations/commandcode/commandcode.ts +75 -115
  344. package/src/providers/integrations/commandcode/quota.ts +131 -51
  345. package/src/providers/integrations/cursor/cursor-http2.ts +0 -3
  346. package/src/providers/integrations/cursor/cursor-quota.ts +2 -6
  347. package/src/providers/integrations/cursor/cursor-request.ts +0 -87
  348. package/src/providers/integrations/cursor/cursor-shared.ts +0 -4
  349. package/src/providers/integrations/cursor/cursor.ts +35 -3
  350. package/src/providers/integrations/cursor/protocol/framing.ts +0 -56
  351. package/src/providers/integrations/cursor/protocol/messages.ts +0 -96
  352. package/src/providers/integrations/factory/factory.ts +9 -9
  353. package/src/providers/integrations/github/github-discovery.ts +70 -12
  354. package/src/providers/integrations/github/github-oauth.ts +184 -14
  355. package/src/providers/integrations/github/github.ts +134 -26
  356. package/src/providers/integrations/grok/grok-oauth.ts +261 -261
  357. package/src/providers/integrations/grok/grok-quota-frame.ts +1 -1
  358. package/src/providers/integrations/grok/grok-quota.ts +16 -7
  359. package/src/providers/integrations/grok/grok-ratelimit-store.ts +0 -5
  360. package/src/providers/integrations/grok/grok-ratelimit.ts +3 -0
  361. package/src/providers/integrations/grok/grok.ts +86 -36
  362. package/src/providers/integrations/kilo/kilo-oauth.ts +1 -1
  363. package/src/providers/integrations/kiro/kiro-cache.ts +31 -0
  364. package/src/providers/integrations/kiro/kiro-catalog.ts +31 -5
  365. package/src/providers/integrations/kiro/kiro-discovery.ts +47 -43
  366. package/src/providers/integrations/kiro/kiro-machine-id.ts +6 -3
  367. package/src/providers/integrations/kiro/kiro-oauth.ts +523 -255
  368. package/src/providers/integrations/kiro/kiro-quota.ts +35 -47
  369. package/src/providers/integrations/kiro/kiro-request.ts +8 -11
  370. package/src/providers/integrations/kiro/kiro-stream.ts +13 -0
  371. package/src/providers/integrations/kiro/kiro.ts +80 -68
  372. package/src/providers/integrations/muse/muse-oauth.ts +0 -1
  373. package/src/providers/integrations/muse/muse-quota.ts +0 -4
  374. package/src/providers/integrations/opencode/fingerprint.ts +7 -5
  375. package/src/providers/integrations/opencode/provider.ts +17 -24
  376. package/src/providers/integrations/qoder/qoder-catalog.ts +94 -11
  377. package/src/providers/integrations/qoder/qoder-oauth.ts +165 -25
  378. package/src/providers/integrations/qoder/qoder-quota.ts +48 -6
  379. package/src/providers/integrations/qoder/qoder-realm.ts +10 -31
  380. package/src/providers/integrations/qoder/qoder-sse.ts +159 -0
  381. package/src/providers/integrations/qoder/qoder-umid.ts +21 -25
  382. package/src/providers/integrations/qoder/qoder-virtual.ts +12 -0
  383. package/src/providers/integrations/qoder/qoder.ts +422 -71
  384. package/src/providers/integrations/shared/connect.ts +0 -54
  385. package/src/providers/integrations/shared/hash.ts +15 -0
  386. package/src/providers/integrations/shared/session-cache.ts +136 -0
  387. package/src/providers/integrations/trae/trae-cache.ts +7 -87
  388. package/src/providers/integrations/trae/trae-catalog.ts +1 -6
  389. package/src/providers/integrations/trae/trae-protocol.ts +17 -8
  390. package/src/providers/integrations/trae/trae-realm.ts +0 -7
  391. package/src/providers/integrations/trae/trae.ts +51 -8
  392. package/src/providers/integrations/xai/xai-oauth.ts +5 -5
  393. package/src/providers/integrations/zcode-start-plan/gifts.ts +265 -0
  394. package/src/providers/integrations/zcode-start-plan/oauth.ts +154 -0
  395. package/src/providers/integrations/zcode-start-plan/provider.ts +131 -0
  396. package/src/providers/integrations/zcode-start-plan/quota.ts +152 -0
  397. package/src/providers/integrations/zcode-start-plan/zcode_prompt.json +10 -0
  398. package/src/providers/operations/account-error-classification.ts +198 -133
  399. package/src/providers/operations/account-health-service.ts +31 -39
  400. package/src/providers/operations/account-reset-service.ts +58 -1
  401. package/src/providers/operations/byok-wire-profile.ts +19 -15
  402. package/src/providers/operations/cli-platform.ts +31 -5
  403. package/src/providers/operations/client-version-resolver.ts +4 -6
  404. package/src/providers/operations/client-versions.ts +10 -7
  405. package/src/providers/operations/provider-catalog-seeder.ts +2 -0
  406. package/src/providers/operations/provider-catalog-service.ts +18 -2
  407. package/src/providers/operations/provider-credential-service.ts +19 -2
  408. package/src/providers/provider-contracts.ts +18 -7
  409. package/src/providers/provider-metadata.ts +17 -2
  410. package/src/providers/provider-model-definition.ts +22 -1
  411. package/src/providers/provider-probe.ts +4 -2
  412. package/src/providers/provider-usage.ts +50 -19
  413. package/src/providers/quota/cache.ts +29 -0
  414. package/src/providers/quota/quota-contracts.ts +4 -0
  415. package/src/providers/quota/refresh.ts +27 -4
  416. package/src/providers/quota/snapshot.ts +94 -0
  417. package/src/providers/search/search-provider.ts +1 -3
  418. package/src/providers/search/search-providers.ts +2 -1
  419. package/src/security/abuse.ts +4 -4
  420. package/src/security/admission/service.ts +13 -6
  421. package/src/security/admission/ttl.ts +61 -61
  422. package/src/security/api-key-auth.ts +17 -3
  423. package/src/security/client-router-fingerprint.ts +4 -4
  424. package/src/security/credential-codec.ts +3 -3
  425. package/src/security/csrf.ts +2 -2
  426. package/src/security/ip-boundary.ts +16 -189
  427. package/src/security/model-access-rule.ts +1 -1
  428. package/src/security/outbound-headers.ts +18 -0
  429. package/src/shared/invoke-guard.ts +47 -0
  430. package/src/transport/contracts.ts +13 -4
  431. package/src/transport/dispatch/attempt-finalize.ts +70 -22
  432. package/src/transport/dispatch/attempt-loop.ts +22 -0
  433. package/src/transport/dispatch/capture-policy.ts +34 -37
  434. package/src/transport/dispatch/empty-stream.ts +23 -20
  435. package/src/transport/dispatch/generation.ts +25 -45
  436. package/src/transport/dispatch/native-shared.ts +127 -0
  437. package/src/transport/dispatch/proxy-request.ts +3 -1
  438. package/src/transport/dispatch/responses-compact.ts +25 -46
  439. package/src/transport/dispatch/streaming/encoder.ts +18 -72
  440. package/src/transport/dispatch/streaming/finalize.ts +2 -2
  441. package/src/transport/dispatch/streaming/stream.ts +7 -3
  442. package/src/transport/dispatch/streaming/thinking-loop.ts +1 -1
  443. package/src/transport/dispatch/systemone.ts +20 -48
  444. package/src/transport/dispatch/token-count.ts +41 -38
  445. package/src/transport/dispatch/websearch.ts +19 -45
  446. package/src/transport/drain-endpoint.ts +1 -1
  447. package/src/transport/failure-policy.ts +21 -1
  448. package/src/transport/guards/auth-guard.ts +0 -6
  449. package/src/transport/guards/body-policy.ts +3 -3
  450. package/src/transport/guards/error-lifecycle.ts +4 -0
  451. package/src/transport/guards/headroom-guard.ts +1 -1
  452. package/src/transport/guards/pipeline.ts +1 -1
  453. package/src/transport/guards/request-context.ts +12 -17
  454. package/src/transport/request/preparer.ts +286 -7
  455. package/src/transport/request/rtk/compress-request.ts +3 -3
  456. package/src/transport/request/rtk/rtk-constants.ts +2 -2
  457. package/src/transport/request/rtk/rtk-filters.ts +8 -8
  458. package/src/transport/request/state.ts +7 -13
  459. package/src/transport/request/system-prompt-injection.ts +74 -11
  460. package/src/transport/request/tenant-preferences.ts +22 -0
  461. package/src/transport/routing/candidate-index.ts +2 -2
  462. package/src/transport/routing/round-robin.ts +1 -1
  463. package/src/transport/routing/route-catalog.ts +17 -18
  464. package/src/transport/routing/route-model.ts +126 -33
  465. package/src/transport/routing/router.ts +159 -37
  466. package/src/transport/shutdown-notice.ts +2 -2
  467. package/src/transport/surface/adapters.ts +3 -3
  468. package/src/transport/surface/chat/encode.ts +7 -7
  469. package/src/transport/surface/chat/parse.ts +7 -7
  470. package/src/transport/surface/completion.ts +2 -2
  471. package/src/transport/surface/content-parts.ts +1 -1
  472. package/src/transport/surface/dialects.ts +2 -2
  473. package/src/transport/surface/messages/parse.ts +3 -3
  474. package/src/transport/surface/reasoning-summary.ts +9 -14
  475. package/src/transport/surface/responses/adapter.ts +1 -1
  476. package/src/transport/surface/responses/errors.ts +0 -9
  477. package/src/transport/vision-handoff-cache.ts +58 -0
  478. package/src/transport/vision-handoff.ts +431 -0
  479. package/src/workers/account-identity-sweep.ts +2 -2
  480. package/src/workers/buddy-growth-tasks.ts +392 -346
  481. package/src/workers/daily-checkin.ts +102 -49
  482. package/src/workers/maintenance-lease.ts +2 -35
  483. package/src/workers/oauth-refresh-sweep.ts +8 -1
  484. package/src/workers/quota-refresh-sweep.ts +120 -82
  485. package/src/workers/scheduler.ts +67 -4
  486. package/src/workers/sweep-runner.ts +24 -5
  487. package/src/workers/zcode-gift-claim.ts +108 -0
  488. package/dist/dashboard/assets/AccountCooldown-Bjo92Qat.js +0 -1
  489. package/dist/dashboard/assets/AccountCooldown-Bjo92Qat.js.gz +0 -0
  490. package/dist/dashboard/assets/BannedPage-ChG5svFb.js +0 -1
  491. package/dist/dashboard/assets/BannedPage-ChG5svFb.js.gz +0 -0
  492. package/dist/dashboard/assets/CliToolDetailPage-BIcWQun7.js +0 -1
  493. package/dist/dashboard/assets/CliToolDetailPage-BIcWQun7.js.gz +0 -0
  494. package/dist/dashboard/assets/CliToolsPage-Bf2VelDe.js +0 -1
  495. package/dist/dashboard/assets/CliToolsPage-Bf2VelDe.js.gz +0 -0
  496. package/dist/dashboard/assets/CombosPage-DCgm5g6g.js +0 -1
  497. package/dist/dashboard/assets/CombosPage-DCgm5g6g.js.gz +0 -0
  498. package/dist/dashboard/assets/ConsoleLogPage-a8mD0QF1.js +0 -1
  499. package/dist/dashboard/assets/ConsoleLogPage-a8mD0QF1.js.gz +0 -0
  500. package/dist/dashboard/assets/CustomizationPage-ggeMSjte.js +0 -1
  501. package/dist/dashboard/assets/CustomizationPage-ggeMSjte.js.gz +0 -0
  502. package/dist/dashboard/assets/HealthEventsModal-DzSWzE_j.js +0 -1
  503. package/dist/dashboard/assets/HealthEventsModal-DzSWzE_j.js.gz +0 -0
  504. package/dist/dashboard/assets/LoginPage-U6AKaJcF.js +0 -1
  505. package/dist/dashboard/assets/LoginPage-U6AKaJcF.js.gz +0 -0
  506. package/dist/dashboard/assets/ModelPicker-BMS2t8BA.js.gz +0 -0
  507. package/dist/dashboard/assets/OverviewPage-D2D4IiHl.js +0 -1
  508. package/dist/dashboard/assets/OverviewPage-D2D4IiHl.js.gz +0 -0
  509. package/dist/dashboard/assets/ProviderDetailPage-CrNf7j_X.js +0 -5
  510. package/dist/dashboard/assets/ProviderDetailPage-CrNf7j_X.js.gz +0 -0
  511. package/dist/dashboard/assets/ProviderIcon-FB6yS4v1.js +0 -1
  512. package/dist/dashboard/assets/ProviderIcon-FB6yS4v1.js.gz +0 -0
  513. package/dist/dashboard/assets/ProvidersPage-BSDbquc1.js +0 -1
  514. package/dist/dashboard/assets/ProvidersPage-BSDbquc1.js.gz +0 -0
  515. package/dist/dashboard/assets/ProxyPage-lsrx7NYg.js.gz +0 -0
  516. package/dist/dashboard/assets/QuotaPage-L0skzML8.js +0 -1
  517. package/dist/dashboard/assets/QuotaPage-L0skzML8.js.gz +0 -0
  518. package/dist/dashboard/assets/SettingsPage-CmAbjRHW.js +0 -1
  519. package/dist/dashboard/assets/SettingsPage-CmAbjRHW.js.gz +0 -0
  520. package/dist/dashboard/assets/SetupPage-f0DE1h0-.js +0 -1
  521. package/dist/dashboard/assets/SetupPage-f0DE1h0-.js.gz +0 -0
  522. package/dist/dashboard/assets/SortableList-ClzpwyET.js +0 -1
  523. package/dist/dashboard/assets/SortableList-ClzpwyET.js.gz +0 -0
  524. package/dist/dashboard/assets/StudioPage-Cq5tW7hW.js.gz +0 -0
  525. package/dist/dashboard/assets/ToolIcon-K1ckcDKg.js +0 -1
  526. package/dist/dashboard/assets/ToolIcon-K1ckcDKg.js.gz +0 -0
  527. package/dist/dashboard/assets/UsagePage-Dfc9rWQn.js +0 -56
  528. package/dist/dashboard/assets/UsagePage-Dfc9rWQn.js.gz +0 -0
  529. package/dist/dashboard/assets/api-keys-C38iXoO4.js +0 -1
  530. package/dist/dashboard/assets/api-keys-C38iXoO4.js.gz +0 -0
  531. package/dist/dashboard/assets/app-BayTTy01.js +0 -9
  532. package/dist/dashboard/assets/app-BayTTy01.js.gz +0 -0
  533. package/dist/dashboard/assets/app-a159k0ub.css +0 -2
  534. package/dist/dashboard/assets/app-a159k0ub.css.gz +0 -0
  535. package/dist/dashboard/assets/assert-DbRA8EG2.js +0 -15
  536. package/dist/dashboard/assets/assert-DbRA8EG2.js.gz +0 -0
  537. package/dist/dashboard/assets/badge-D9CS6dZk.js.gz +0 -0
  538. package/dist/dashboard/assets/banner-DympBNq4.js.gz +0 -0
  539. package/dist/dashboard/assets/card-ibRWU_uT.js.gz +0 -0
  540. package/dist/dashboard/assets/cli-tools-63qBGaRw.js +0 -1
  541. package/dist/dashboard/assets/cli-tools-63qBGaRw.js.gz +0 -0
  542. package/dist/dashboard/assets/clipboard-button-DJm-9pz7.js.gz +0 -0
  543. package/dist/dashboard/assets/dialog-8fiE6tec.js +0 -1
  544. package/dist/dashboard/assets/dialog-8fiE6tec.js.gz +0 -0
  545. package/dist/dashboard/assets/drawer-CqGYeOrd.js.gz +0 -0
  546. package/dist/dashboard/assets/entry-BSAsYsmV.js +0 -1
  547. package/dist/dashboard/assets/entry-BSAsYsmV.js.gz +0 -0
  548. package/dist/dashboard/assets/entry-CiIQULZD.js +0 -4
  549. package/dist/dashboard/assets/entry-CiIQULZD.js.gz +0 -0
  550. package/dist/dashboard/assets/entry-Daz0lJ-W.js +0 -1
  551. package/dist/dashboard/assets/entry-Daz0lJ-W.js.gz +0 -0
  552. package/dist/dashboard/assets/formatters-D-MTJxMr.js.gz +0 -0
  553. package/dist/dashboard/assets/github-badge-CPK47xQx.js +0 -1
  554. package/dist/dashboard/assets/github-badge-CPK47xQx.js.gz +0 -0
  555. package/dist/dashboard/assets/index-DBNPOfhV.js.gz +0 -0
  556. package/dist/dashboard/assets/inline-BT0zntEr.js.gz +0 -0
  557. package/dist/dashboard/assets/input-Da2ZViWg.js.gz +0 -0
  558. package/dist/dashboard/assets/layout-BXNeLw6M.js.gz +0 -0
  559. package/dist/dashboard/assets/list-row-BycOrrIV.js.gz +0 -0
  560. package/dist/dashboard/assets/live-DCQNZpF7.js +0 -1
  561. package/dist/dashboard/assets/live-DCQNZpF7.js.gz +0 -0
  562. package/dist/dashboard/assets/mutation-Rr0TIvcj.js.gz +0 -0
  563. package/dist/dashboard/assets/network-9Yj2ZyLo.js +0 -1
  564. package/dist/dashboard/assets/network-9Yj2ZyLo.js.gz +0 -0
  565. package/dist/dashboard/assets/numbers-CEagj6ZZ.js +0 -2
  566. package/dist/dashboard/assets/numbers-CEagj6ZZ.js.gz +0 -0
  567. package/dist/dashboard/assets/providers/zai-coding-plan.svg +0 -6
  568. package/dist/dashboard/assets/providers/zai-coding-plan.svg.gz +0 -0
  569. package/dist/dashboard/assets/quota-Caw7wqci.js +0 -1
  570. package/dist/dashboard/assets/quota-Caw7wqci.js.gz +0 -0
  571. package/dist/dashboard/assets/routing-CO9zCUus.js +0 -1
  572. package/dist/dashboard/assets/routing-CO9zCUus.js.gz +0 -0
  573. package/dist/dashboard/assets/select-BIYIBkMU.js.gz +0 -0
  574. package/dist/dashboard/assets/settings-jS3-PNSA.js +0 -1
  575. package/dist/dashboard/assets/settings-jS3-PNSA.js.gz +0 -0
  576. package/dist/dashboard/assets/stack-DfgnFDWY.js.gz +0 -0
  577. package/dist/dashboard/assets/state-CkfdYWel.js.gz +0 -0
  578. package/dist/dashboard/assets/stream-retry-5QnVuSSo.js +0 -1
  579. package/dist/dashboard/assets/stream-retry-5QnVuSSo.js.gz +0 -0
  580. package/dist/dashboard/assets/switch-SYm237HG.js.gz +0 -0
  581. package/dist/dashboard/assets/useMutation-BQqCRNIi.js.gz +0 -0
  582. package/src/console/cli-tools/injectors/cline.ts +0 -110
  583. package/src/console/cli-tools/injectors/copilot.ts +0 -108
  584. package/src/console/cli-tools/injectors/cowork.ts +0 -64
  585. package/src/console/cli-tools/injectors/deepseek-tui.ts +0 -85
  586. package/src/console/cli-tools/injectors/droid.ts +0 -96
  587. package/src/console/cli-tools/injectors/grok-build.ts +0 -70
  588. package/src/console/cli-tools/injectors/hermes.ts +0 -101
  589. package/src/console/cli-tools/injectors/jcode.ts +0 -115
  590. package/src/console/cli-tools/injectors/openclaw.ts +0 -161
  591. package/src/network/response-headers.ts +0 -39
  592. package/src/protocol/response/dsml.ts +0 -38
  593. package/src/providers/integrations/xiaomi-mimo/think-stream.ts +0 -229
  594. /package/dist/dashboard/assets/brand/{og_bansos.webp → og_bansos_initial.webp} +0 -0
  595. /package/dist/dashboard/assets/brand/{og_image.webp → og_image_initial.webp} +0 -0
@@ -36,8 +36,7 @@ import {
36
36
  type ProxyCooldownEntry,
37
37
  } from "./pool-state";
38
38
 
39
- export type { PoolRotation, PoolUsageSnapshot, ProxyCooldownEntry };
40
- export { parseCooldownEntry };
39
+ export type { PoolRotation, ProxyCooldownEntry };
41
40
 
42
41
  // Pool selector
43
42
  /**
@@ -51,7 +50,7 @@ export { parseCooldownEntry };
51
50
  */
52
51
  export const PROVIDER_BLOCK_MS = 10 * 365 * 24 * 60 * 60 * 1000;
53
52
 
54
- export const DEFAULT_PROXY_CONCURRENCY = 10;
53
+ const DEFAULT_PROXY_CONCURRENCY = 10;
55
54
  const DEFAULT_PROXY_WEIGHT = 100;
56
55
  const MAX_PROXY_WEIGHT = 1000;
57
56
  // Crash-recovery bound, not a lease duration: normal releases DECR/DEL the
@@ -63,7 +62,7 @@ const MAX_PROXY_WEIGHT = 1000;
63
62
  function poolInflightTtlSeconds(): number {
64
63
  return resolveInflightTtlSeconds();
65
64
  }
66
- export function effectiveConcurrency(limit: number): number {
65
+ function effectiveConcurrency(limit: number): number {
67
66
  if (!Number.isInteger(limit) || limit < 1) return 0;
68
67
  return limit;
69
68
  }
@@ -100,7 +99,7 @@ function decrementLocal(counter: Map<string, number>, key: string): void {
100
99
  else counter.set(key, value - 1);
101
100
  }
102
101
 
103
- export type PoolSelectionFailureReason =
102
+ type PoolSelectionFailureReason =
104
103
  | "at_capacity"
105
104
  | "provider_at_capacity"
106
105
  | "cooldown"
@@ -109,7 +108,7 @@ export type PoolSelectionFailureReason =
109
108
  | "coordination_unavailable"
110
109
  | "no_active_pool";
111
110
 
112
- export interface PoolCapacitySnapshot {
111
+ interface PoolCapacitySnapshot {
113
112
  readonly poolId: string;
114
113
  readonly maxInflight: number;
115
114
  readonly currentInflight: number;
@@ -206,67 +205,67 @@ export class NetworkPoolSelector {
206
205
  }
207
206
 
208
207
  /**
209
- * Raise this provider's share of a pool, enforcing both the pool ceiling and
210
- * the provider ceiling. The pool half is still checked here so a caller that
211
- * already admitted the pool through `acquire` does not double-count it — use
212
- * this instead of `acquire` whenever a provider ceiling applies.
208
+ * One local slot acquisition: checks every map's ceiling, bumps each counter,
209
+ * and returns a release that decrements all of them exactly once. Shared by
210
+ * `acquire` (single pool counter) and `acquireProvider` (pool + provider
211
+ * share) so the release closure and the local bookkeeping live in one place.
213
212
  */
214
- acquireProvider(
215
- poolId: string,
216
- providerId: string,
217
- poolCapacity: number,
218
- providerCapacity: number,
213
+ private acquireLocalSlot(
214
+ increments: ReadonlyArray<{
215
+ readonly map: Map<string, number>;
216
+ readonly key: string;
217
+ readonly capacity: number;
218
+ }>,
219
219
  ): { acquired: boolean; release: () => void } {
220
- const current = this.getInflight(poolId);
221
- if (current >= poolCapacity) return { acquired: false, release: () => {} };
222
- const key = providerInflightStateKey(poolId, providerId);
223
- const providerCurrent = this.state.providerInflight.get(key) ?? 0;
224
- if (providerCurrent >= providerCapacity) return { acquired: false, release: () => {} };
225
-
226
- enforceInflightLimit(this.state.inflight);
227
- enforceInflightLimit(this.state.providerInflight);
228
- this.state.inflight.set(poolId, current + 1);
229
- this.state.providerInflight.set(key, providerCurrent + 1);
220
+ for (const increment of increments) {
221
+ const current = increment.map.get(increment.key) ?? 0;
222
+ if (current >= increment.capacity) return { acquired: false, release: () => {} };
223
+ }
224
+ for (const increment of increments) {
225
+ enforceInflightLimit(increment.map);
226
+ increment.map.set(increment.key, (increment.map.get(increment.key) ?? 0) + 1);
227
+ }
230
228
  this.notifyPoolUsage();
231
229
 
232
230
  let released = false;
233
231
  const release = () => {
234
232
  if (released) return;
235
233
  released = true;
236
- decrementLocal(this.state.inflight, poolId);
237
- decrementLocal(this.state.providerInflight, key);
234
+ for (const increment of increments) decrementLocal(increment.map, increment.key);
238
235
  this.notifyPoolUsage();
239
236
  };
240
237
  return { acquired: true, release };
241
238
  }
242
239
 
240
+ /**
241
+ * Raise this provider's share of a pool, enforcing both the pool ceiling and
242
+ * the provider ceiling. The pool half is still checked here so a caller that
243
+ * already admitted the pool through `acquire` does not double-count it — use
244
+ * this instead of `acquire` whenever a provider ceiling applies.
245
+ */
246
+ acquireProvider(
247
+ poolId: string,
248
+ providerId: string,
249
+ poolCapacity: number,
250
+ providerCapacity: number,
251
+ ): { acquired: boolean; release: () => void } {
252
+ return this.acquireLocalSlot([
253
+ { map: this.state.inflight, key: poolId, capacity: poolCapacity },
254
+ {
255
+ map: this.state.providerInflight,
256
+ key: providerInflightStateKey(poolId, providerId),
257
+ capacity: providerCapacity,
258
+ },
259
+ ]);
260
+ }
261
+
243
262
  acquire(
244
263
  poolId: string,
245
264
  maxInflight = DEFAULT_PROXY_CONCURRENCY,
246
265
  ): { acquired: boolean; release: () => void } {
247
- const current = this.getInflight(poolId);
248
- if (current >= maxInflight) {
249
- return { acquired: false, release: () => {} };
250
- }
251
-
252
- enforceInflightLimit(this.state.inflight);
253
- this.state.inflight.set(poolId, current + 1);
254
- this.notifyPoolUsage();
255
- let released = false;
256
-
257
- const release = () => {
258
- if (released) return;
259
- released = true;
260
- const val = this.state.inflight.get(poolId) ?? 1;
261
- if (val <= 1) {
262
- this.state.inflight.delete(poolId);
263
- } else {
264
- this.state.inflight.set(poolId, val - 1);
265
- }
266
- this.notifyPoolUsage();
267
- };
268
-
269
- return { acquired: true, release };
266
+ return this.acquireLocalSlot([
267
+ { map: this.state.inflight, key: poolId, capacity: maxInflight },
268
+ ]);
270
269
  }
271
270
 
272
271
  private cooldownKey(poolId: string, providerId: string): string {
@@ -577,6 +576,98 @@ export class NetworkPoolSelector {
577
576
  await this.redis.del(indexKey);
578
577
  }
579
578
 
579
+ /**
580
+ * Tries one local candidate: bump the local counters (pool, and provider
581
+ * share when a ceiling is set) and return its release. `undefined` when the
582
+ * candidate is full or the provider share is exhausted.
583
+ */
584
+ private localAcquire(
585
+ candidate: { readonly poolId: string; readonly capacity: number },
586
+ providerId: string,
587
+ providerCapacity: number | undefined,
588
+ ): { poolId: string; release: () => void } | undefined {
589
+ const slot =
590
+ providerCapacity === undefined
591
+ ? this.acquire(candidate.poolId, candidate.capacity)
592
+ : this.acquireProvider(candidate.poolId, providerId, candidate.capacity, providerCapacity);
593
+ return slot.acquired ? { poolId: candidate.poolId, release: slot.release } : undefined;
594
+ }
595
+
596
+ /**
597
+ * One atomic Redis admit (the compare-and-increment script) for a candidate:
598
+ * bumps both the remote and local counters on success and returns a release
599
+ * that decrements both. `undefined` when the pool or provider share is full
600
+ * (1 admitted; 0 pool full; 2 provider full — either full result fails over
601
+ * to the next pool, which is the whole point of the ceiling).
602
+ */
603
+ private async redisAdmit(
604
+ redis: RedisClient,
605
+ candidate: { readonly poolId: string; readonly capacity: number },
606
+ providerId: string,
607
+ providerCapacity: number | undefined,
608
+ ): Promise<{ poolId: string; release: () => void } | undefined> {
609
+ const providerKey =
610
+ providerCapacity === undefined
611
+ ? undefined
612
+ : proxyProviderInflightKey(candidate.poolId, providerId);
613
+ const result =
614
+ providerKey === undefined
615
+ ? await redisEvalNumber(
616
+ redis,
617
+ POOL_ADMIT_SCRIPT,
618
+ 1,
619
+ proxyInflightKey(candidate.poolId),
620
+ String(candidate.capacity),
621
+ String(poolInflightTtlSeconds()),
622
+ )
623
+ : await redisEvalNumber(
624
+ redis,
625
+ POOL_PROVIDER_ADMIT_SCRIPT,
626
+ 2,
627
+ proxyInflightKey(candidate.poolId),
628
+ providerKey,
629
+ String(candidate.capacity),
630
+ String(providerCapacity),
631
+ String(poolInflightTtlSeconds()),
632
+ );
633
+ if (result !== 1) return undefined;
634
+ enforceInflightLimit(this.state.inflight);
635
+ this.state.inflight.set(candidate.poolId, this.getInflight(candidate.poolId) + 1);
636
+ if (providerKey !== undefined) {
637
+ enforceInflightLimit(this.state.providerInflight);
638
+ const localProviderKey = providerInflightStateKey(candidate.poolId, providerId);
639
+ this.state.providerInflight.set(
640
+ localProviderKey,
641
+ this.getProviderInflight(candidate.poolId, providerId) + 1,
642
+ );
643
+ }
644
+ this.notifyPoolUsage();
645
+ let released = false;
646
+ return {
647
+ poolId: candidate.poolId,
648
+ release: () => {
649
+ if (released) return;
650
+ released = true;
651
+ decrementLocal(this.state.inflight, candidate.poolId);
652
+ if (providerKey !== undefined) {
653
+ decrementLocal(
654
+ this.state.providerInflight,
655
+ providerInflightStateKey(candidate.poolId, providerId),
656
+ );
657
+ }
658
+ this.notifyPoolUsage();
659
+ const script = providerKey === undefined ? POOL_RELEASE_SCRIPT : POOL_PROVIDER_RELEASE_SCRIPT;
660
+ const keys =
661
+ providerKey === undefined
662
+ ? [proxyInflightKey(candidate.poolId)]
663
+ : [proxyInflightKey(candidate.poolId), providerKey];
664
+ void redisEvalNumber(redis, script, keys.length, ...keys).catch((error: unknown) => {
665
+ log.error("[pool-selector] failed to release distributed slot", error as Error);
666
+ });
667
+ },
668
+ };
669
+ }
670
+
580
671
  /**
581
672
  * Distributed pool admission — the selection algorithm:
582
673
  *
@@ -652,17 +743,8 @@ export class NetworkPoolSelector {
652
743
  shuffleArray(scored);
653
744
  for (const candidate of scored) {
654
745
  if (this.getInflight(candidate.poolId) >= candidate.capacity) continue;
655
- const slot =
656
- providerCapacity === undefined
657
- ? this.acquire(candidate.poolId, candidate.capacity)
658
- : this.acquireProvider(
659
- candidate.poolId,
660
- providerId,
661
- candidate.capacity,
662
- providerCapacity,
663
- );
664
- if (!slot.acquired) continue;
665
- return { poolId: candidate.poolId, release: slot.release };
746
+ const slot = this.localAcquire(candidate, providerId, providerCapacity);
747
+ if (slot) return slot;
666
748
  }
667
749
  return undefined;
668
750
  }
@@ -677,18 +759,10 @@ export class NetworkPoolSelector {
677
759
  const candidate = scored[absolute];
678
760
  if (!candidate) continue;
679
761
  if (this.getInflight(candidate.poolId) >= candidate.capacity) continue;
680
- const slot =
681
- providerCapacity === undefined
682
- ? this.acquire(candidate.poolId, candidate.capacity)
683
- : this.acquireProvider(
684
- candidate.poolId,
685
- providerId,
686
- candidate.capacity,
687
- providerCapacity,
688
- );
689
- if (!slot.acquired) continue;
762
+ const slot = this.localAcquire(candidate, providerId, providerCapacity);
763
+ if (!slot) continue;
690
764
  advanceRotation(this.state.rotationCursors, rotation.key, scored.length, absolute, rotation.rotateCount);
691
- return { poolId: candidate.poolId, release: slot.release };
765
+ return slot;
692
766
  }
693
767
  return undefined;
694
768
  }
@@ -716,11 +790,7 @@ export class NetworkPoolSelector {
716
790
  }
717
791
  }
718
792
  if (!bestPool) return undefined;
719
- const slot =
720
- providerCapacity === undefined
721
- ? this.acquire(bestPool.poolId, bestPool.capacity)
722
- : this.acquireProvider(bestPool.poolId, providerId, bestPool.capacity, providerCapacity);
723
- return slot.acquired ? { poolId: bestPool.poolId, release: slot.release } : undefined;
793
+ return this.localAcquire(bestPool, providerId, providerCapacity);
724
794
  }
725
795
 
726
796
  const redis = this.redis;
@@ -795,34 +865,8 @@ export class NetworkPoolSelector {
795
865
  });
796
866
  }
797
867
  for (const candidate of withCounts) {
798
- const providerKey =
799
- providerCapacity === undefined
800
- ? undefined
801
- : proxyProviderInflightKey(candidate.poolId, providerId);
802
- const result =
803
- providerKey === undefined
804
- ? await redisEvalNumber(
805
- redis,
806
- POOL_ADMIT_SCRIPT,
807
- 1,
808
- proxyInflightKey(candidate.poolId),
809
- String(candidate.capacity),
810
- String(poolInflightTtlSeconds()),
811
- )
812
- : await redisEvalNumber(
813
- redis,
814
- POOL_PROVIDER_ADMIT_SCRIPT,
815
- 2,
816
- proxyInflightKey(candidate.poolId),
817
- providerKey,
818
- String(candidate.capacity),
819
- String(providerCapacity),
820
- String(poolInflightTtlSeconds()),
821
- );
822
- // 1 admitted; 0 pool full; 2 provider full. Either full result fails over
823
- // to the next pool, which is the whole point of the ceiling: the request
824
- // moves to an egress that still has room for this provider.
825
- if (result !== 1) continue;
868
+ const admitted = await this.redisAdmit(redis, candidate, providerId, providerCapacity);
869
+ if (admitted === undefined) continue;
826
870
  if (rotation && rotation.strategy !== "random") {
827
871
  advanceRotation(
828
872
  this.state.rotationCursors,
@@ -832,37 +876,7 @@ export class NetworkPoolSelector {
832
876
  rotation.rotateCount,
833
877
  );
834
878
  }
835
- enforceInflightLimit(this.state.inflight);
836
- this.state.inflight.set(candidate.poolId, this.getInflight(candidate.poolId) + 1);
837
- if (providerKey !== undefined) {
838
- enforceInflightLimit(this.state.providerInflight);
839
- const localProviderKey = providerInflightStateKey(candidate.poolId, providerId);
840
- this.state.providerInflight.set(
841
- localProviderKey,
842
- this.getProviderInflight(candidate.poolId, providerId) + 1,
843
- );
844
- }
845
- this.notifyPoolUsage();
846
- let released = false;
847
- return {
848
- poolId: candidate.poolId,
849
- release: () => {
850
- if (released) return;
851
- released = true;
852
- decrementLocal(this.state.inflight, candidate.poolId);
853
- if (providerKey !== undefined) {
854
- decrementLocal(this.state.providerInflight, providerInflightStateKey(candidate.poolId, providerId));
855
- }
856
- this.notifyPoolUsage();
857
- const script = providerKey === undefined ? POOL_RELEASE_SCRIPT : POOL_PROVIDER_RELEASE_SCRIPT;
858
- const keys = providerKey === undefined ? [proxyInflightKey(candidate.poolId)] : [proxyInflightKey(candidate.poolId), providerKey];
859
- void redisEvalNumber(redis, script, keys.length, ...keys).catch(
860
- (error: unknown) => {
861
- log.error("[pool-selector] failed to release distributed slot", error as Error);
862
- },
863
- );
864
- },
865
- };
879
+ return admitted;
866
880
  }
867
881
  return undefined;
868
882
  }
@@ -16,7 +16,7 @@ export interface PoolHealthEvent {
16
16
  readonly createdAt: string;
17
17
  }
18
18
 
19
- export interface PoolHealthSnapshot {
19
+ interface PoolHealthSnapshot {
20
20
  readonly poolId: string;
21
21
  readonly tenantId: string;
22
22
  readonly status: "active" | "cooldown" | "disabled";
@@ -21,7 +21,7 @@ import { GatewayError } from "../protocol/gateway-error";
21
21
  import { providerRateLimitIsIpScoped } from "../providers/provider-metadata";
22
22
 
23
23
  /** What one upstream failure means for the egress that carried it. */
24
- export type EgressVerdict =
24
+ type EgressVerdict =
25
25
  | { kind: "none" }
26
26
  | { kind: "cooldown"; retryAfterMs?: number }
27
27
  | { kind: "blocked" };
@@ -9,7 +9,7 @@
9
9
  import { exponentialBackoff, type BackoffOptions } from "./backoff";
10
10
  import { sleep as defaultSleep } from "../shared/sleep";
11
11
 
12
- export interface RetryOptions {
12
+ interface RetryOptions {
13
13
  readonly maxRetries?: number;
14
14
  readonly baseDelayMs?: number;
15
15
  readonly maxDelayMs?: number;
@@ -3,6 +3,12 @@ import { lookup } from "node:dns/promises";
3
3
  import { isIP } from "node:net";
4
4
  import { GatewayError } from "../protocol/gateway-error";
5
5
  import type { SsrfPolicy } from "../config/network";
6
+ import {
7
+ cidrContainsAddress,
8
+ ipv4ToNumberStrict,
9
+ ipv6GroupsToBytes,
10
+ normalizeIpv6Groups,
11
+ } from "./ip-utils";
6
12
 
7
13
  const DNS_CACHE_TTL_MS = 60_000;
8
14
  const DNS_CACHE_MAX = 64;
@@ -31,87 +37,24 @@ type DnsLookup = (
31
37
  options: { all: true },
32
38
  ) => Promise<readonly { address: string; family: number }[]>;
33
39
 
34
- function ipv4Number(address: string): number {
35
- const parts = address.split(".");
36
- if (parts.length !== 4 || parts.some((part) => !/^\d+$/.test(part)))
37
- throw new Error("invalid IPv4");
38
- const octets = parts.map(Number);
39
- if (octets.some((part) => part < 0 || part > 255)) throw new Error("invalid IPv4");
40
- return (((octets[0]! << 24) >>> 0) | (octets[1]! << 16) | (octets[2]! << 8) | octets[3]!) >>> 0;
41
- }
42
-
43
40
  function ipv6Bytes(address: string): Uint8Array {
44
- const value = address.toLowerCase().split("%", 1)[0]!;
45
- if (value.includes(".")) {
46
- const split = value.lastIndexOf(":");
47
- if (split < 0) throw new Error("invalid IPv6");
48
- const mapped = ipv4Number(value.slice(split + 1));
49
- return ipv6Bytes(
50
- `${value.slice(0, split)}:${(mapped >>> 16).toString(16)}:${(mapped & 0xffff).toString(16)}`,
51
- );
52
- }
53
- const halves = value.split("::");
54
- if (halves.length > 2) throw new Error("invalid IPv6");
55
- const left = halves[0] ? halves[0].split(":") : [];
56
- const right = halves.length === 2 && halves[1] ? halves[1].split(":") : [];
57
- if (halves.length === 1 && left.length !== 8) throw new Error("invalid IPv6");
58
- const missing = 8 - left.length - right.length;
59
- if (missing < 0 || (halves.length === 2 && missing < 1)) throw new Error("invalid IPv6");
60
- const groups = [...left, ...Array.from({ length: missing }, () => "0"), ...right];
61
- if (groups.length !== 8 || groups.some((part) => !/^[0-9a-f]{1,4}$/.test(part)))
62
- throw new Error("invalid IPv6");
63
- const bytes = new Uint8Array(16);
64
- groups.forEach((part, index) => {
65
- const n = Number.parseInt(part, 16);
66
- bytes[index * 2] = n >>> 8;
67
- bytes[index * 2 + 1] = n & 255;
68
- });
41
+ const bytes = ipv6GroupsToBytes(normalizeIpv6Groups(address, { allowDottedQuad: true }) ?? []);
42
+ if (bytes === undefined) throw new Error("invalid IPv6");
69
43
  return bytes;
70
44
  }
71
45
 
72
46
  function cidrContains(address: string, cidr: string): boolean {
73
- const [network, prefixText] = cidr.split("/");
74
- if (!network || prefixText === undefined) return false;
75
- // A present-but-blank prefix (`"10.0.0.0/"`) must be refused, not coerced.
76
- // `Number("")` is 0 and `""` is not `undefined`, so a plain truthiness check
77
- // here would leave `prefix = 0` — and a /0 admits every address of its family.
78
- // An operator's typo would therefore silently widen the allowlist to the whole
79
- // IPv4 space, including the link-local metadata endpoint this guard exists to
80
- // refuse.
81
- //
82
- // Requiring a run of digits (after trimming) also refuses the exotic spellings
83
- // `Number` accepts for zero — `-0`, `+0`, `0x0`, `0b0`, `0o0`, `0.0` — all of
84
- // which are typos rather than a deliberate "allow everything". Only a written
85
- // `0` means that, which is the next branch's business.
86
- const prefixDigits = prefixText.trim();
87
- if (!/^\d+$/.test(prefixDigits)) return false;
88
- const prefix = Number(prefixDigits);
89
- const family = isIP(address);
90
- if (
91
- family !== isIP(network) ||
92
- !Number.isInteger(prefix) ||
93
- prefix < 0 ||
94
- prefix > (family === 4 ? 32 : 128)
95
- )
96
- return false;
97
- if (family === 4) {
98
- const bits = 32 - prefix;
99
- return bits === 32 ? true : ipv4Number(address) >>> bits === ipv4Number(network) >>> bits;
100
- }
101
- const a = ipv6Bytes(address);
102
- const n = ipv6Bytes(network);
103
- for (let index = 0; index < prefix; index++) {
104
- const byte = Math.floor(index / 8);
105
- const bit = 7 - (index % 8);
106
- if (((a[byte]! >> bit) & 1) !== ((n[byte]! >> bit) & 1)) return false;
107
- }
108
- return true;
47
+ return cidrContainsAddress(address, cidr, {
48
+ requirePrefix: true,
49
+ unwrapMapped: false,
50
+ allowDottedQuad: true,
51
+ });
109
52
  }
110
53
 
111
54
  function unsafeAddress(address: string): boolean {
112
55
  const family = isIP(address);
113
56
  if (family === 4) {
114
- const n = ipv4Number(address);
57
+ const n = ipv4ToNumberStrict(address);
115
58
  return [
116
59
  [0, 8],
117
60
  [0x0a000000, 8],
@@ -175,9 +118,15 @@ export function validateResolvedAddresses(
175
118
  return addresses[0]!;
176
119
  }
177
120
 
121
+ export async function resolveAllAddresses(hostname: string): Promise<string[]>;
178
122
  export async function resolveAllAddresses(
179
123
  hostname: string,
180
124
  signal: AbortSignal,
125
+ lookupFn?: DnsLookup,
126
+ ): Promise<string[]>;
127
+ export async function resolveAllAddresses(
128
+ hostname: string,
129
+ signal: AbortSignal = new AbortController().signal,
181
130
  lookupFn: DnsLookup = lookup,
182
131
  ): Promise<string[]> {
183
132
  if (signal.aborted) throw abortErrorForSignal(signal);
@@ -57,6 +57,12 @@ export interface TelemetryEventRecord {
57
57
  readonly lastEventAtMs: number | null;
58
58
  /** Why the drawer payloads are empty, or `captured`; absent when unrecorded. */
59
59
  readonly payloadCaptureState?: string | null;
60
+ /**
61
+ * True for a completed request whose output was far smaller than the model's
62
+ * ceiling and which ended with an ordinary `stop` (a "degenerate stop").
63
+ * NULL/unset for everything else.
64
+ */
65
+ readonly degenerateStop?: boolean;
60
66
  }
61
67
 
62
68
  function asString(value: unknown): string | null {
@@ -109,6 +115,7 @@ function normalize(raw: Record<string, unknown>): TelemetryEventRecord | undefin
109
115
  tokensPerSec: asNumber(raw["tokensPerSec"]),
110
116
  firstContentDeltaAtMs: asNumber(raw["firstContentDeltaAtMs"]),
111
117
  lastEventAtMs: asNumber(raw["lastEventAtMs"]),
118
+ ...(raw["degenerateStop"] === true ? { degenerateStop: true } : {}),
112
119
  };
113
120
  }
114
121
 
@@ -255,7 +262,7 @@ export async function readTelemetryEvents(): Promise<TelemetryEventRecord[]> {
255
262
  return events;
256
263
  }
257
264
 
258
- export interface EventQuery {
265
+ interface EventQuery {
259
266
  readonly tenantId?: string;
260
267
  readonly since?: Date;
261
268
  /**
@@ -372,7 +379,7 @@ export async function queryTelemetryEvents(query: EventQuery): Promise<Telemetry
372
379
  return events;
373
380
  }
374
381
 
375
- export interface EventTotals {
382
+ interface EventTotals {
376
383
  requests: number;
377
384
  succeeded: number;
378
385
  failed: number;