cartethyia 2.7.3 → 2.7.5

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 (625) hide show
  1. package/.env.example +2 -0
  2. package/README.md +17 -5
  3. package/dist/dashboard/assets/{BannedPage-2TJD37Hb.js → BannedPage-Cj15GsMp.js} +1 -1
  4. package/dist/dashboard/assets/BannedPage-Cj15GsMp.js.gz +0 -0
  5. package/dist/dashboard/assets/ClaimGiftsButton-D83pBJ6J.js +1 -0
  6. package/dist/dashboard/assets/ClaimGiftsButton-D83pBJ6J.js.gz +0 -0
  7. package/dist/dashboard/assets/{CliToolDetailPage-xefJNRmJ.js → CliToolDetailPage-DZKmVuJN.js} +1 -1
  8. package/dist/dashboard/assets/CliToolDetailPage-DZKmVuJN.js.gz +0 -0
  9. package/dist/dashboard/assets/{CliToolsPage-Rj9iHQ3F.js → CliToolsPage-BIccZmeL.js} +1 -1
  10. package/dist/dashboard/assets/CliToolsPage-BIccZmeL.js.gz +0 -0
  11. package/dist/dashboard/assets/CombosPage-DEEKkYD-.js +1 -0
  12. package/dist/dashboard/assets/CombosPage-DEEKkYD-.js.gz +0 -0
  13. package/dist/dashboard/assets/ConsoleLogPage-D5rol2j2.js +1 -0
  14. package/dist/dashboard/assets/ConsoleLogPage-D5rol2j2.js.gz +0 -0
  15. package/dist/dashboard/assets/CustomizationPage-BKj_r0rp.js +1 -0
  16. package/dist/dashboard/assets/CustomizationPage-BKj_r0rp.js.gz +0 -0
  17. package/dist/dashboard/assets/InjectSystemPromptPanel-B5nZvmf0.js +656 -0
  18. package/dist/dashboard/assets/InjectSystemPromptPanel-B5nZvmf0.js.gz +0 -0
  19. package/dist/dashboard/assets/{LoginPage-C4w7vea-.js → LoginPage-DkXb6dx-.js} +1 -1
  20. package/dist/dashboard/assets/LoginPage-DkXb6dx-.js.gz +0 -0
  21. package/dist/dashboard/assets/{ModelPicker-BtPcAfot.js → ModelPicker-DnQPSIA8.js} +1 -1
  22. package/dist/dashboard/assets/ModelPicker-DnQPSIA8.js.gz +0 -0
  23. package/dist/dashboard/assets/OverviewPage-Ci2wQKUz.js +1 -0
  24. package/dist/dashboard/assets/OverviewPage-Ci2wQKUz.js.gz +0 -0
  25. package/dist/dashboard/assets/ProviderDetailPage-odlgYrOg.js +5 -0
  26. package/dist/dashboard/assets/ProviderDetailPage-odlgYrOg.js.gz +0 -0
  27. package/dist/dashboard/assets/ProviderIcon-EfeloBn4.js +1 -0
  28. package/dist/dashboard/assets/ProviderIcon-EfeloBn4.js.gz +0 -0
  29. package/dist/dashboard/assets/ProvidersPage-CqFBQEB8.js +1 -0
  30. package/dist/dashboard/assets/ProvidersPage-CqFBQEB8.js.gz +0 -0
  31. package/dist/dashboard/assets/ProxyPage-5SnUSy3j.js +9 -0
  32. package/dist/dashboard/assets/ProxyPage-5SnUSy3j.js.gz +0 -0
  33. package/dist/dashboard/assets/QuotaPage-CmnMDzhP.js +1 -0
  34. package/dist/dashboard/assets/QuotaPage-CmnMDzhP.js.gz +0 -0
  35. package/dist/dashboard/assets/SettingsPage-BJtRZ4qD.js +1 -0
  36. package/dist/dashboard/assets/SettingsPage-BJtRZ4qD.js.gz +0 -0
  37. package/dist/dashboard/assets/{SetupPage-BQS0Rx_u.js → SetupPage-WTa0sktC.js} +1 -1
  38. package/dist/dashboard/assets/SetupPage-WTa0sktC.js.gz +0 -0
  39. package/dist/dashboard/assets/{StudioPage-TP7voAQ0.js → StudioPage-BfAb5-o_.js} +3 -3
  40. package/dist/dashboard/assets/StudioPage-BfAb5-o_.js.gz +0 -0
  41. package/dist/dashboard/assets/UsagePage-BNzeZ2Vk.js +56 -0
  42. package/dist/dashboard/assets/UsagePage-BNzeZ2Vk.js.gz +0 -0
  43. package/dist/dashboard/assets/api-keys-l29JROPv.js +1 -0
  44. package/dist/dashboard/assets/api-keys-l29JROPv.js.gz +0 -0
  45. package/dist/dashboard/assets/app--PJWq8_C.css +2 -0
  46. package/dist/dashboard/assets/app--PJWq8_C.css.gz +0 -0
  47. package/dist/dashboard/assets/{assert-C0YYuZJ4.js → assert-C0K-c4eo.js} +1 -1
  48. package/dist/dashboard/assets/assert-C0K-c4eo.js.gz +0 -0
  49. package/dist/dashboard/assets/{badge-B_DMpMJN.js → badge-DfbhQU4k.js} +1 -1
  50. package/dist/dashboard/assets/badge-DfbhQU4k.js.gz +0 -0
  51. package/dist/dashboard/assets/{banner-DMrMPmth.js → banner-BllT4LcF.js} +1 -1
  52. package/dist/dashboard/assets/banner-BllT4LcF.js.gz +0 -0
  53. package/dist/dashboard/assets/brand/login_register.jpg +0 -0
  54. package/dist/dashboard/assets/brand/login_register_initial.jpg +0 -0
  55. package/dist/dashboard/assets/brand/og_bansos.jpg +0 -0
  56. package/dist/dashboard/assets/brand/og_image.jpg +0 -0
  57. package/dist/dashboard/assets/{card-Be5Uzamx.js → card-CtkX66Wd.js} +1 -1
  58. package/dist/dashboard/assets/card-CtkX66Wd.js.gz +0 -0
  59. package/dist/dashboard/assets/chevron-left-Bf6CaTSJ.js +1 -0
  60. package/dist/dashboard/assets/chevron-left-Bf6CaTSJ.js.gz +0 -0
  61. package/dist/dashboard/assets/{cli-tools-muFRphDu.js → cli-tools-CQXVMn4P.js} +1 -1
  62. package/dist/dashboard/assets/cli-tools-CQXVMn4P.js.gz +0 -0
  63. package/dist/dashboard/assets/{clipboard-button-DOfrVmsi.js → clipboard-button-BQqBQd2n.js} +1 -1
  64. package/dist/dashboard/assets/clipboard-button-BQqBQd2n.js.gz +0 -0
  65. package/dist/dashboard/assets/{dialog-C9omdjQt.js → dialog-BEK7KP0H.js} +1 -1
  66. package/dist/dashboard/assets/dialog-BEK7KP0H.js.gz +0 -0
  67. package/dist/dashboard/assets/{drawer-Bm8WKaGY.js → drawer-BrTEZ8zu.js} +1 -1
  68. package/dist/dashboard/assets/drawer-BrTEZ8zu.js.gz +0 -0
  69. package/dist/dashboard/assets/entry-CHWxbS56.js +1 -0
  70. package/dist/dashboard/assets/entry-CHWxbS56.js.gz +0 -0
  71. package/dist/dashboard/assets/{entry-q5HwZ8nN.js → entry-EBHJ3DrE.js} +1 -1
  72. package/dist/dashboard/assets/entry-EBHJ3DrE.js.gz +0 -0
  73. package/dist/dashboard/assets/entry-LCS85hiq.js +4 -0
  74. package/dist/dashboard/assets/entry-LCS85hiq.js.gz +0 -0
  75. package/dist/dashboard/assets/{formatters-BTScrQpe.js → formatters-BAACA727.js} +1 -1
  76. package/dist/dashboard/assets/formatters-BAACA727.js.gz +0 -0
  77. package/dist/dashboard/assets/{github-badge-Orjuzemr.js → github-badge-YSCE4UC2.js} +1 -1
  78. package/dist/dashboard/assets/github-badge-YSCE4UC2.js.gz +0 -0
  79. package/dist/dashboard/assets/{index-cPrX5vn6.js → index-FQlj0HUT.js} +2 -2
  80. package/dist/dashboard/assets/index-FQlj0HUT.js.gz +0 -0
  81. package/dist/dashboard/assets/{inline-Dvux8w-0.js → inline-C4gU5iOL.js} +1 -1
  82. package/dist/dashboard/assets/inline-C4gU5iOL.js.gz +0 -0
  83. package/dist/dashboard/assets/{input-CU1Rgf3p.js → input-BHHX_Tmm.js} +1 -1
  84. package/dist/dashboard/assets/input-BHHX_Tmm.js.gz +0 -0
  85. package/dist/dashboard/assets/{layout-C_7A6H55.js → layout-CajrYUnD.js} +1 -1
  86. package/dist/dashboard/assets/{layout-C_7A6H55.js.gz → layout-CajrYUnD.js.gz} +0 -0
  87. package/dist/dashboard/assets/{list-row-CzFvogbj.js → list-row-Bx2I7XUe.js} +1 -1
  88. package/dist/dashboard/assets/list-row-Bx2I7XUe.js.gz +0 -0
  89. package/dist/dashboard/assets/live-DNNqRbj2.js +1 -0
  90. package/dist/dashboard/assets/live-DNNqRbj2.js.gz +0 -0
  91. package/dist/dashboard/assets/minimize-2-BTmMTK4o.js +1 -0
  92. package/dist/dashboard/assets/minimize-2-BTmMTK4o.js.gz +0 -0
  93. package/dist/dashboard/assets/{mutation-JJGQm8TD.js → mutation-Rr0TIvcj.js} +1 -1
  94. package/dist/dashboard/assets/mutation-Rr0TIvcj.js.gz +0 -0
  95. package/dist/dashboard/assets/network-C5UBACrJ.js +1 -0
  96. package/dist/dashboard/assets/network-C5UBACrJ.js.gz +0 -0
  97. package/dist/dashboard/assets/numbers-CUWAZK4J.js +2 -0
  98. package/dist/dashboard/assets/numbers-CUWAZK4J.js.gz +0 -0
  99. package/dist/dashboard/assets/pagination-Xsi9Qx04.js +1 -0
  100. package/dist/dashboard/assets/pagination-Xsi9Qx04.js.gz +0 -0
  101. package/dist/dashboard/assets/{query-keys-CLaNz8Xz.js → query-keys-BloKtaXJ.js} +1 -1
  102. package/dist/dashboard/assets/query-keys-BloKtaXJ.js.gz +0 -0
  103. package/dist/dashboard/assets/quota-DvQbD_sr.js +1 -0
  104. package/dist/dashboard/assets/quota-DvQbD_sr.js.gz +0 -0
  105. package/dist/dashboard/assets/{routing-BEH32QOC.js → routing-in0f_IfK.js} +1 -1
  106. package/dist/dashboard/assets/routing-in0f_IfK.js.gz +0 -0
  107. package/dist/dashboard/assets/{select-B3iTetHN.js → select-BLULNqTp.js} +2 -2
  108. package/dist/dashboard/assets/select-BLULNqTp.js.gz +0 -0
  109. package/dist/dashboard/assets/settings-CcGczj6I.js +1 -0
  110. package/dist/dashboard/assets/settings-CcGczj6I.js.gz +0 -0
  111. package/dist/dashboard/assets/shield-BMQNxORh.js +1 -0
  112. package/dist/dashboard/assets/shield-BMQNxORh.js.gz +0 -0
  113. package/dist/dashboard/assets/{stack-KrOrm7JZ.js → stack-D2lIR0tg.js} +1 -1
  114. package/dist/dashboard/assets/stack-D2lIR0tg.js.gz +0 -0
  115. package/dist/dashboard/assets/{state-DLS7IDMy.js → state-DTARjfiW.js} +1 -1
  116. package/dist/dashboard/assets/state-DTARjfiW.js.gz +0 -0
  117. package/dist/dashboard/assets/{switch-42ItvbAs.js → switch-c0oPSnqW.js} +1 -1
  118. package/dist/dashboard/assets/switch-c0oPSnqW.js.gz +0 -0
  119. package/dist/dashboard/assets/{useMutation-Dmnfh1Ue.js → useMutation-m8WGNPrz.js} +1 -1
  120. package/dist/dashboard/assets/useMutation-m8WGNPrz.js.gz +0 -0
  121. package/dist/dashboard/console-backgrounds.jpg +0 -0
  122. package/dist/dashboard/console-backgrounds_initial.jpg +0 -0
  123. package/dist/dashboard/index.html +2 -2
  124. package/dist/dashboard/index.html.gz +0 -0
  125. package/dist/dashboard/precompressed-assets.json +59 -57
  126. package/migrations/full-postgres/0027_qoder_identity_genuine_seed.sql +9 -10
  127. package/migrations/full-postgres/0032_add_provider_pool_accounts.sql +53 -0
  128. package/migrations/full-postgres/0033_add_api_key_ban.sql +9 -0
  129. package/migrations/full-postgres/0034_add_api_key_ban_until.sql +7 -0
  130. package/migrations/full-postgres/0035_align_pool_account_status_enum.sql +21 -0
  131. package/migrations/full-postgres/0036_system_prompt_presets_refs.sql +10 -0
  132. package/migrations/full-postgres/0037_share_concurrency_scope.sql +3 -0
  133. package/migrations/full-postgres/0038_add_accounts_quota_snapshot.sql +7 -0
  134. package/migrations/full-postgres/0039_add_telemetry_provider_model_indexes.sql +13 -0
  135. package/migrations/full-postgres/0040_add_models_dialect.sql +3 -0
  136. package/migrations/full-postgres/0041_add_daily_checkin_ledger_account_fk.sql +7 -0
  137. package/migrations/lite-sqlite3/0027_qoder_identity_genuine_seed.sql +9 -10
  138. package/migrations/lite-sqlite3/0032_add_provider_pool_accounts.sql +44 -0
  139. package/migrations/lite-sqlite3/0033_add_api_key_ban.sql +8 -0
  140. package/migrations/lite-sqlite3/0034_add_api_key_ban_until.sql +5 -0
  141. package/migrations/lite-sqlite3/0035_align_pool_account_status_enum.sql +6 -0
  142. package/migrations/lite-sqlite3/0036_system_prompt_presets_refs.sql +14 -0
  143. package/migrations/lite-sqlite3/0037_share_concurrency_scope.sql +3 -0
  144. package/migrations/lite-sqlite3/0038_add_accounts_quota_snapshot.sql +4 -0
  145. package/migrations/lite-sqlite3/0039_add_telemetry_provider_model_indexes.sql +6 -0
  146. package/migrations/lite-sqlite3/0040_add_models_dialect.sql +3 -0
  147. package/migrations/lite-sqlite3/0041_add_daily_checkin_ledger_account_fk.sql +26 -0
  148. package/package.json +4 -5
  149. package/scripts/cli/update.ts +15 -41
  150. package/src/boot/app.ts +67 -10
  151. package/src/boot/dependencies.ts +786 -732
  152. package/src/boot/lifecycle.ts +11 -12
  153. package/src/config/cooldowns.ts +34 -55
  154. package/src/config/network.ts +8 -4
  155. package/src/config/package-paths.ts +1 -1
  156. package/src/config/providers.ts +13 -14
  157. package/src/config/resources.ts +0 -5
  158. package/src/config/runtime.ts +2 -5
  159. package/src/config/spec.ts +26 -6
  160. package/src/console/api-keys/ban-constants.ts +38 -0
  161. package/src/console/api-keys/contracts.ts +92 -31
  162. package/src/console/api-keys/routes.ts +131 -6
  163. package/src/console/audit/routes.ts +3 -3
  164. package/src/console/auth/access.ts +4 -4
  165. package/src/console/auth/account-reset.ts +5 -5
  166. package/src/console/auth/audit-recorder.ts +1 -1
  167. package/src/console/auth/lockout.ts +7 -3
  168. package/src/console/auth/row-coercion.ts +57 -12
  169. package/src/console/auth/session-resolver.ts +1 -1
  170. package/src/console/auth/session-service.ts +8 -2
  171. package/src/console/auth/session.ts +40 -34
  172. package/src/console/auth/setup-service.ts +4 -3
  173. package/src/console/auth/user-store.ts +1 -1
  174. package/src/console/backup/9router-map.ts +0 -3
  175. package/src/console/backup/contracts.ts +11 -9
  176. package/src/console/backup/export-endpoint.ts +11 -12
  177. package/src/console/backup/nine-router.ts +1 -7
  178. package/src/console/backup/routes.ts +32 -36
  179. package/src/console/backup/service.ts +468 -468
  180. package/src/console/backup/store.ts +47 -29
  181. package/src/console/backup/validate.ts +11 -1
  182. package/src/console/cli-tools/contracts.ts +7 -7
  183. package/src/console/cli-tools/injectors/cline.ts +110 -110
  184. package/src/console/cli-tools/injectors/codex.ts +181 -181
  185. package/src/console/cli-tools/injectors/copilot.ts +108 -108
  186. package/src/console/cli-tools/injectors/driver.ts +247 -247
  187. package/src/console/cli-tools/injectors/jcode.ts +115 -115
  188. package/src/console/cli-tools/injectors/openclaw.ts +161 -161
  189. package/src/console/cli-tools/injectors/opencode.ts +153 -153
  190. package/src/console/cli-tools/routes.ts +4 -4
  191. package/src/console/cli-tools/service.ts +1 -1
  192. package/src/console/cli-tools/store.ts +2 -2
  193. package/src/console/console-router-guards.ts +2 -2
  194. package/src/console/console-router.ts +36 -4
  195. package/src/console/dashboard-assets.ts +9 -9
  196. package/src/console/data-management/routes.ts +3 -3
  197. package/src/console/domain-registration.ts +18 -4
  198. package/src/console/inferhub/client.ts +0 -7
  199. package/src/console/inferhub/key-store.ts +1 -1
  200. package/src/console/inferhub/normalize.ts +2 -2
  201. package/src/console/inferhub/routes.ts +1 -1
  202. package/src/console/inferhub/types.ts +1 -1
  203. package/src/console/observability/contracts.ts +5 -1
  204. package/src/console/observability/live.ts +34 -21
  205. package/src/console/observability/logs.ts +4 -4
  206. package/src/console/observability/performance.ts +3 -3
  207. package/src/console/observability/routes.ts +3 -3
  208. package/src/console/observability/sse.ts +62 -8
  209. package/src/console/observability/store.ts +36 -20
  210. package/src/console/providers/catalog/catalog-projections.ts +1 -1
  211. package/src/console/providers/catalog/contracts.ts +82 -22
  212. package/src/console/providers/catalog/model-operations.ts +1 -1
  213. package/src/console/providers/catalog/provider-operations.ts +110 -17
  214. package/src/console/providers/catalog/public-model-store.ts +0 -0
  215. package/src/console/providers/catalog/routes.ts +30 -6
  216. package/src/console/providers/catalog/store-helpers.ts +349 -326
  217. package/src/console/providers/catalog/store.ts +1662 -1292
  218. package/src/console/providers/detail/routes.ts +4 -4
  219. package/src/console/providers/oauth/callback-listener.ts +100 -18
  220. package/src/console/providers/oauth/routes.ts +95 -16
  221. package/src/console/quota/account-quota-global.ts +41 -3
  222. package/src/console/quota/account-quota-shared.ts +10 -3
  223. package/src/console/quota/account-quota-tenant.ts +285 -67
  224. package/src/console/quota/account-test-egress.ts +4 -4
  225. package/src/console/quota/daily-growth-job.ts +308 -0
  226. package/src/console/quota/native-identity.ts +2 -2
  227. package/src/console/quota/quota-view.ts +1 -1
  228. package/src/console/remote-support/routes.ts +1 -1
  229. package/src/console/remote-support/service.ts +2 -2
  230. package/src/console/routing/model/contracts.ts +1 -1
  231. package/src/console/routing/model/routes.ts +3 -3
  232. package/src/console/routing/pools/contracts.ts +13 -10
  233. package/src/console/routing/pools/import-parse.ts +3 -3
  234. package/src/console/routing/pools/operations.ts +14 -7
  235. package/src/console/routing/pools/probe-verdict.ts +8 -3
  236. package/src/console/routing/pools/relay-deploy.ts +12 -12
  237. package/src/console/routing/pools/routes.ts +3 -3
  238. package/src/console/routing/pools/store.ts +21 -1
  239. package/src/console/settings/contracts.ts +29 -2
  240. package/src/console/settings/routes.ts +3 -3
  241. package/src/console/settings/store.ts +20 -0
  242. package/src/console/share/share-router.ts +112 -44
  243. package/src/console/share/share-stats.ts +109 -39
  244. package/src/console/share/share-usage.ts +1 -1
  245. package/src/console/shared/errors.ts +2 -2
  246. package/src/console/shared/object.ts +18 -0
  247. package/src/console/shared/read-through-cache.ts +42 -6
  248. package/src/console/shared/transaction.ts +14 -0
  249. package/src/console/system-prompt/contracts.ts +89 -12
  250. package/src/console/system-prompt/routes.ts +98 -45
  251. package/src/console/updates/apply-update.ts +244 -0
  252. package/src/console/updates/routes.ts +256 -98
  253. package/src/main.ts +11 -5
  254. package/src/network/http2-bidi.ts +1 -1
  255. package/src/network/ip-utils.ts +298 -0
  256. package/src/network/outbound-fetch.ts +76 -5
  257. package/src/network/pool/agent.ts +7 -6
  258. package/src/network/pool/byte-accounting.ts +1 -1
  259. package/src/network/pool/resolver.ts +55 -51
  260. package/src/network/pool/selector.ts +150 -136
  261. package/src/network/pool-cooldown-store.ts +71 -16
  262. package/src/network/pool-health-machine.ts +22 -2
  263. package/src/network/rate-limit-verdict.ts +68 -0
  264. package/src/network/retry.ts +1 -1
  265. package/src/network/ssrf.ts +24 -75
  266. package/src/observability/event-log.ts +2 -2
  267. package/src/observability/event-query.ts +286 -214
  268. package/src/observability/log-sanitize.ts +1 -1
  269. package/src/observability/metrics.ts +4 -4
  270. package/src/observability/payload-capture.ts +6 -5
  271. package/src/observability/payload-store.ts +1 -1
  272. package/src/observability/runtime-metrics.ts +1 -1
  273. package/src/observability/telemetry-buffer.ts +12 -3
  274. package/src/observability/telemetry-reconciliation.ts +3 -3
  275. package/src/observability/telemetry-status.ts +1 -1
  276. package/src/observability/token-speed.ts +1 -1
  277. package/src/persistence/connection-url.ts +1 -1
  278. package/src/persistence/db-handle.ts +1 -1
  279. package/src/persistence/db-pg.ts +2 -2
  280. package/src/persistence/db-sqlite.ts +10 -5
  281. package/src/persistence/dialect.ts +5 -1
  282. package/src/persistence/readiness.ts +19 -7
  283. package/src/persistence/redis.ts +1 -1
  284. package/src/persistence/schema.ts +177 -45
  285. package/src/persistence/shared-cache.ts +2 -2
  286. package/src/persistence/sort-order.ts +1 -1
  287. package/src/persistence/stores/api-key-store.ts +109 -4
  288. package/src/persistence/stores/share-store.ts +20 -8
  289. package/src/persistence/stores/telemetry-store.ts +1 -1
  290. package/src/persistence/system-prompt-store.ts +63 -18
  291. package/src/persistence/tenant-preferences.ts +1 -1
  292. package/src/persistence/ttl-cache.ts +3 -3
  293. package/src/persistence/usage-flush.ts +7 -2
  294. package/src/protocol/canonical-model.ts +1 -1
  295. package/src/protocol/codecs.ts +12 -16
  296. package/src/protocol/dsml.ts +3 -49
  297. package/src/protocol/gateway-error.ts +6 -3
  298. package/src/protocol/image-clamp.ts +2 -2
  299. package/src/protocol/image-generation.ts +2 -2
  300. package/src/protocol/reasoning-text.ts +1 -1
  301. package/src/protocol/reasoning.ts +7 -52
  302. package/src/protocol/registry.ts +50 -15
  303. package/src/protocol/request/chat.ts +46 -19
  304. package/src/protocol/request/codex.ts +1 -1
  305. package/src/protocol/request/messages.ts +46 -14
  306. package/src/protocol/request/responses.ts +19 -10
  307. package/src/protocol/response/chat.ts +27 -12
  308. package/src/protocol/response/codex.ts +2 -2
  309. package/src/protocol/response/gemini.ts +1 -1
  310. package/src/protocol/response/messages.ts +6 -6
  311. package/src/protocol/response/omp-tool-call-markup.ts +1 -1
  312. package/src/protocol/response/responses.ts +3 -3
  313. package/src/protocol/sse-decoder.ts +64 -4
  314. package/src/protocol/stop-reason.ts +2 -2
  315. package/src/protocol/think-split.ts +347 -0
  316. package/src/protocol/translation/attachment-integrity.ts +3 -3
  317. package/src/protocol/translation/cache-controls.ts +8 -20
  318. package/src/protocol/translation/capabilities.ts +8 -8
  319. package/src/protocol/translation/dialect/deepseek.ts +10 -0
  320. package/src/protocol/translation/dialect/inband.ts +39 -0
  321. package/src/protocol/translation/dialect/model-thinking.ts +89 -0
  322. package/src/protocol/translation/dialect/qwen.ts +114 -0
  323. package/src/protocol/translation/dialect/resolver.ts +83 -0
  324. package/src/protocol/translation/dialect/rules.ts +93 -0
  325. package/src/protocol/translation/dialect/thinking-ladder.ts +245 -0
  326. package/src/protocol/translation/dialect/types.ts +58 -0
  327. package/src/protocol/translation/quirks.ts +18 -4
  328. package/src/protocol/translation/thinking.ts +12 -252
  329. package/src/protocol/translation/tool-id.ts +3 -3
  330. package/src/protocol/translation/tool-repair.ts +1 -1
  331. package/src/protocol/upstream/post-json.ts +6 -1
  332. package/src/protocol/upstream/send-claude-messages.ts +3 -3
  333. package/src/providers/adapters/base-adapter.ts +1 -1
  334. package/src/providers/adapters/compatible-adapter.ts +44 -5
  335. package/src/providers/authentication/jwt-validator.ts +0 -5
  336. package/src/providers/authentication/oauth-client.ts +254 -253
  337. package/src/providers/authentication/oauth-flow-store.ts +42 -7
  338. package/src/providers/authentication/oauth-refresh-service.ts +2 -2
  339. package/src/providers/bundled-discovery-providers.ts +6 -30
  340. package/src/providers/configured-openai-providers.ts +82 -97
  341. package/src/providers/configured-provider.ts +1 -1
  342. package/src/providers/default-registry.ts +23 -35
  343. package/src/providers/discovery/catalog/catalog-format.ts +13 -2
  344. package/src/providers/discovery/discovery-types.ts +0 -1
  345. package/src/providers/discovery/openai-model-discovery.ts +2 -2
  346. package/src/providers/discovery/probe-phases.ts +50 -15
  347. package/src/providers/discovery/probing-service.ts +18 -20
  348. package/src/providers/integrations/agentrouter/agentrouter.ts +14 -5
  349. package/src/providers/integrations/alibabacp/alibabacp.ts +2 -2
  350. package/src/providers/integrations/anthropic.ts +4 -11
  351. package/src/providers/integrations/antigravity/antigravity-oauth.ts +0 -1
  352. package/src/providers/integrations/antigravity/antigravity-protocol.ts +0 -14
  353. package/src/providers/integrations/antigravity/antigravity.ts +15 -26
  354. package/src/providers/integrations/buddy/buddy-catalog-shared.ts +2 -2
  355. package/src/providers/integrations/buddy/buddy-chat-shared.ts +20 -4
  356. package/src/providers/integrations/buddy/buddy-cli-headers.ts +19 -52
  357. package/src/providers/integrations/buddy/buddy-discovery-shared.ts +37 -6
  358. package/src/providers/integrations/buddy/buddy-identity.ts +108 -1
  359. package/src/providers/integrations/buddy/buddy-media.ts +16 -16
  360. package/src/providers/integrations/buddy/buddy-oauth-shared.ts +1 -1
  361. package/src/providers/integrations/buddy/codebuddy/cn-provider.ts +1 -2
  362. package/src/providers/integrations/buddy/codebuddy/provider.ts +5 -16
  363. package/src/providers/integrations/buddy/codebuddy/shared.ts +30 -9
  364. package/src/providers/integrations/buddy/workbuddy/provider.ts +6 -10
  365. package/src/providers/integrations/buddy/workbuddy/shared.ts +21 -3
  366. package/src/providers/integrations/claude/claude-betas.ts +1 -1
  367. package/src/providers/integrations/claude/claude-cch.ts +148 -148
  368. package/src/providers/integrations/claude/claude-credentials.ts +1 -1
  369. package/src/providers/integrations/claude/claude-slow-lane-retry.ts +1 -1
  370. package/src/providers/integrations/claude/claude.ts +3 -10
  371. package/src/providers/integrations/claude/fallback-credit.ts +3 -7
  372. package/src/providers/integrations/cline/cline.ts +2 -2
  373. package/src/providers/integrations/cloudflareai/cloudflareai.ts +4 -7
  374. package/src/providers/integrations/codex/codex-device-code.ts +101 -101
  375. package/src/providers/integrations/codex/codex-errors.ts +1 -1
  376. package/src/providers/integrations/codex/codex-oauth.ts +0 -1
  377. package/src/providers/integrations/codex/codex.ts +14 -15
  378. package/src/providers/integrations/commandcode/commandcode.ts +76 -116
  379. package/src/providers/integrations/commandcode/quota.ts +132 -48
  380. package/src/providers/integrations/cursor/cursor-http2.ts +11 -14
  381. package/src/providers/integrations/cursor/cursor-quota.ts +2 -6
  382. package/src/providers/integrations/cursor/cursor-request.ts +0 -87
  383. package/src/providers/integrations/cursor/cursor-shared.ts +0 -4
  384. package/src/providers/integrations/cursor/cursor.ts +53 -20
  385. package/src/providers/integrations/cursor/protocol/framing.ts +0 -56
  386. package/src/providers/integrations/cursor/protocol/messages.ts +0 -96
  387. package/src/providers/integrations/factory/factory.ts +13 -13
  388. package/src/providers/integrations/gemini.ts +11 -11
  389. package/src/providers/integrations/github/github-discovery.ts +55 -10
  390. package/src/providers/integrations/github/github-oauth.ts +6 -5
  391. package/src/providers/integrations/github/github.ts +47 -20
  392. package/src/providers/integrations/grok/grok-oauth.ts +261 -261
  393. package/src/providers/integrations/grok/grok-quota-frame.ts +1 -1
  394. package/src/providers/integrations/grok/grok-quota.ts +16 -7
  395. package/src/providers/integrations/grok/grok-ratelimit-store.ts +0 -5
  396. package/src/providers/integrations/grok/grok-ratelimit.ts +3 -0
  397. package/src/providers/integrations/grok/grok.ts +88 -42
  398. package/src/providers/integrations/inferhub.ts +4 -11
  399. package/src/providers/integrations/kilo/kilo-oauth.ts +1 -1
  400. package/src/providers/integrations/kimi/kimi.ts +4 -11
  401. package/src/providers/integrations/kiro/kiro-cache.ts +31 -0
  402. package/src/providers/integrations/kiro/kiro-catalog.ts +31 -5
  403. package/src/providers/integrations/kiro/kiro-discovery.ts +47 -43
  404. package/src/providers/integrations/kiro/kiro-machine-id.ts +6 -3
  405. package/src/providers/integrations/kiro/kiro-oauth.ts +523 -255
  406. package/src/providers/integrations/kiro/kiro-quota.ts +35 -47
  407. package/src/providers/integrations/kiro/kiro-request.ts +8 -11
  408. package/src/providers/integrations/kiro/kiro-stream.ts +13 -0
  409. package/src/providers/integrations/kiro/kiro.ts +91 -78
  410. package/src/providers/integrations/muse/muse-oauth.ts +0 -1
  411. package/src/providers/integrations/muse/muse-quota.ts +0 -4
  412. package/src/providers/integrations/muse/muse.ts +6 -9
  413. package/src/providers/integrations/opencode/fingerprint.ts +7 -5
  414. package/src/providers/integrations/opencode/provider.ts +17 -24
  415. package/src/providers/integrations/perplexity.ts +2 -2
  416. package/src/providers/integrations/qoder/qoder-catalog.ts +132 -17
  417. package/src/providers/integrations/qoder/qoder-checkin.ts +26 -8
  418. package/src/providers/integrations/qoder/qoder-native-identity-store.ts +9 -8
  419. package/src/providers/integrations/qoder/qoder-oauth.ts +165 -25
  420. package/src/providers/integrations/qoder/qoder-quota.ts +48 -6
  421. package/src/providers/integrations/qoder/qoder-realm.ts +22 -28
  422. package/src/providers/integrations/qoder/qoder-sse.ts +159 -0
  423. package/src/providers/integrations/qoder/qoder-umid.ts +33 -38
  424. package/src/providers/integrations/qoder/qoder-virtual.ts +14 -2
  425. package/src/providers/integrations/qoder/qoder.ts +521 -63
  426. package/src/providers/integrations/shared/connect.ts +0 -54
  427. package/src/providers/integrations/shared/hash.ts +15 -0
  428. package/src/providers/integrations/shared/session-cache.ts +136 -0
  429. package/src/providers/integrations/tokenharbor.ts +2 -2
  430. package/src/providers/integrations/trae/trae-cache.ts +7 -87
  431. package/src/providers/integrations/trae/trae-catalog.ts +1 -6
  432. package/src/providers/integrations/trae/trae-protocol.ts +17 -8
  433. package/src/providers/integrations/trae/trae-realm.ts +0 -7
  434. package/src/providers/integrations/trae/trae.ts +59 -19
  435. package/src/providers/integrations/xai/xai-oauth.ts +5 -5
  436. package/src/providers/integrations/zcode/oauth.ts +1 -1
  437. package/src/providers/integrations/zcode-start-plan/gifts.ts +265 -0
  438. package/src/providers/integrations/zcode-start-plan/oauth.ts +154 -0
  439. package/src/providers/integrations/zcode-start-plan/provider.ts +131 -0
  440. package/src/providers/integrations/zcode-start-plan/quota.ts +152 -0
  441. package/src/providers/integrations/zcode-start-plan/zcode_prompt.json +10 -0
  442. package/src/providers/operations/account-error-classification.ts +131 -116
  443. package/src/providers/operations/account-health-service.ts +31 -39
  444. package/src/providers/operations/account-reset-service.ts +58 -1
  445. package/src/providers/operations/cli-platform.ts +31 -5
  446. package/src/providers/operations/client-version-resolver.ts +4 -6
  447. package/src/providers/operations/client-versions.ts +10 -7
  448. package/src/providers/operations/provider-catalog-seeder.ts +2 -0
  449. package/src/providers/operations/provider-catalog-service.ts +22 -6
  450. package/src/providers/operations/provider-credential-service.ts +35 -6
  451. package/src/providers/operations/upstream-deadline.ts +3 -3
  452. package/src/providers/provider-contracts.ts +65 -10
  453. package/src/providers/provider-credential-resolver.ts +1 -1
  454. package/src/providers/provider-metadata.ts +17 -2
  455. package/src/providers/provider-model-definition.ts +22 -1
  456. package/src/providers/provider-probe.ts +4 -2
  457. package/src/providers/provider-registry.ts +4 -4
  458. package/src/providers/provider-usage.ts +81 -16
  459. package/src/providers/quota/cache.ts +29 -0
  460. package/src/providers/quota/quota-contracts.ts +28 -10
  461. package/src/providers/quota/refresh.ts +29 -6
  462. package/src/providers/quota/snapshot.ts +94 -0
  463. package/src/providers/search/search-provider.ts +6 -8
  464. package/src/providers/search/search-providers.ts +2 -1
  465. package/src/security/abuse.ts +8 -8
  466. package/src/security/admission/reasons.ts +17 -17
  467. package/src/security/admission/service.ts +2 -2
  468. package/src/security/admission/ttl.ts +61 -61
  469. package/src/security/api-key-auth.ts +20 -8
  470. package/src/security/client-router-fingerprint.ts +4 -4
  471. package/src/security/credential-codec.ts +3 -3
  472. package/src/security/csrf.ts +2 -2
  473. package/src/security/ip-boundary.ts +16 -189
  474. package/src/security/model-access-rule.ts +1 -1
  475. package/src/security/outbound-headers.ts +18 -0
  476. package/src/shared/coerce.ts +19 -0
  477. package/src/shared/invoke-guard.ts +47 -0
  478. package/src/transport/contracts.ts +13 -4
  479. package/src/transport/dispatch/attempt-finalize.ts +5 -4
  480. package/src/transport/dispatch/attempt-loop.ts +10 -7
  481. package/src/transport/dispatch/capture-policy.ts +34 -37
  482. package/src/transport/dispatch/empty-stream.ts +28 -25
  483. package/src/transport/dispatch/generation.ts +33 -53
  484. package/src/transport/dispatch/leases.ts +8 -8
  485. package/src/transport/dispatch/native-shared.ts +127 -0
  486. package/src/transport/dispatch/proxy-request.ts +10 -8
  487. package/src/transport/dispatch/responses-compact.ts +35 -56
  488. package/src/transport/dispatch/retry-policy.ts +4 -67
  489. package/src/transport/dispatch/stream-bridge.ts +1 -1
  490. package/src/transport/dispatch/streaming/encoder.ts +18 -72
  491. package/src/transport/dispatch/streaming/finalize.ts +11 -10
  492. package/src/transport/dispatch/streaming/stream.ts +7 -3
  493. package/src/transport/dispatch/streaming/thinking-loop.ts +1 -1
  494. package/src/transport/dispatch/systemone.ts +27 -55
  495. package/src/transport/dispatch/token-count.ts +49 -46
  496. package/src/transport/dispatch/websearch.ts +24 -50
  497. package/src/transport/drain-endpoint.ts +3 -3
  498. package/src/transport/failure-policy.ts +19 -6
  499. package/src/transport/guards/auth-guard.ts +6 -11
  500. package/src/transport/guards/body-policy.ts +7 -7
  501. package/src/transport/guards/error-lifecycle.ts +18 -3
  502. package/src/transport/guards/guard-errors.ts +7 -3
  503. package/src/transport/guards/headroom-guard.ts +2 -2
  504. package/src/transport/guards/pipeline.ts +7 -7
  505. package/src/transport/guards/readiness-guard.ts +16 -2
  506. package/src/transport/guards/request-context.ts +15 -20
  507. package/src/transport/request/preparer.ts +306 -27
  508. package/src/transport/request/rtk/compress-request.ts +3 -3
  509. package/src/transport/request/rtk/rtk-constants.ts +2 -2
  510. package/src/transport/request/rtk/rtk-filters.ts +8 -8
  511. package/src/transport/request/state.ts +5 -20
  512. package/src/transport/request/system-prompt-injection.ts +74 -11
  513. package/src/transport/request/tenant-preferences.ts +22 -0
  514. package/src/transport/routing/candidate-index.ts +2 -2
  515. package/src/transport/routing/round-robin.ts +1 -1
  516. package/src/transport/routing/route-catalog.ts +113 -19
  517. package/src/transport/routing/route-model.ts +131 -38
  518. package/src/transport/routing/router.ts +154 -39
  519. package/src/transport/shutdown-notice.ts +21 -9
  520. package/src/transport/surface/adapters.ts +3 -3
  521. package/src/transport/surface/chat/encode.ts +7 -7
  522. package/src/transport/surface/chat/parse.ts +7 -7
  523. package/src/transport/surface/completion.ts +3 -3
  524. package/src/transport/surface/content-parts.ts +1 -1
  525. package/src/transport/surface/dialects.ts +2 -2
  526. package/src/transport/surface/messages/encode.ts +1 -1
  527. package/src/transport/surface/messages/parse.ts +3 -3
  528. package/src/transport/surface/responses/adapter.ts +1 -1
  529. package/src/transport/surface/responses/errors.ts +0 -9
  530. package/src/transport/vision-handoff-cache.ts +58 -0
  531. package/src/transport/vision-handoff.ts +376 -0
  532. package/src/workers/account-identity-sweep.ts +2 -2
  533. package/src/workers/buddy-growth-tasks.ts +392 -346
  534. package/src/workers/daily-checkin.ts +102 -49
  535. package/src/workers/maintenance-lease.ts +2 -35
  536. package/src/workers/oauth-refresh-sweep.ts +8 -1
  537. package/src/workers/quota-refresh-sweep.ts +120 -82
  538. package/src/workers/scheduler.ts +67 -4
  539. package/src/workers/sweep-runner.ts +24 -5
  540. package/src/workers/zcode-gift-claim.ts +108 -0
  541. package/dist/dashboard/assets/AccountCooldown-Bjo92Qat.js +0 -1
  542. package/dist/dashboard/assets/AccountCooldown-Bjo92Qat.js.gz +0 -0
  543. package/dist/dashboard/assets/BannedPage-2TJD37Hb.js.gz +0 -0
  544. package/dist/dashboard/assets/CliToolDetailPage-xefJNRmJ.js.gz +0 -0
  545. package/dist/dashboard/assets/CliToolsPage-Rj9iHQ3F.js.gz +0 -0
  546. package/dist/dashboard/assets/CombosPage-JEbYJFxw.js +0 -1
  547. package/dist/dashboard/assets/CombosPage-JEbYJFxw.js.gz +0 -0
  548. package/dist/dashboard/assets/ConsoleLogPage-C3xx_inM.js +0 -1
  549. package/dist/dashboard/assets/ConsoleLogPage-C3xx_inM.js.gz +0 -0
  550. package/dist/dashboard/assets/CustomizationPage-Dw306gJU.js +0 -1
  551. package/dist/dashboard/assets/CustomizationPage-Dw306gJU.js.gz +0 -0
  552. package/dist/dashboard/assets/HealthEventsModal-jaBo1TWV.js +0 -1
  553. package/dist/dashboard/assets/HealthEventsModal-jaBo1TWV.js.gz +0 -0
  554. package/dist/dashboard/assets/LoginPage-C4w7vea-.js.gz +0 -0
  555. package/dist/dashboard/assets/ModelPicker-BtPcAfot.js.gz +0 -0
  556. package/dist/dashboard/assets/OverviewPage-CGC5gNs1.js +0 -1
  557. package/dist/dashboard/assets/OverviewPage-CGC5gNs1.js.gz +0 -0
  558. package/dist/dashboard/assets/ProviderDetailPage-DQcthNGt.js +0 -4
  559. package/dist/dashboard/assets/ProviderDetailPage-DQcthNGt.js.gz +0 -0
  560. package/dist/dashboard/assets/ProviderIcon-FB6yS4v1.js +0 -1
  561. package/dist/dashboard/assets/ProviderIcon-FB6yS4v1.js.gz +0 -0
  562. package/dist/dashboard/assets/ProvidersPage-0OsXRGXe.js +0 -1
  563. package/dist/dashboard/assets/ProvidersPage-0OsXRGXe.js.gz +0 -0
  564. package/dist/dashboard/assets/ProxyPage-DdPc8slb.js +0 -9
  565. package/dist/dashboard/assets/ProxyPage-DdPc8slb.js.gz +0 -0
  566. package/dist/dashboard/assets/QuotaPage-Ci5O4BnH.js +0 -1
  567. package/dist/dashboard/assets/QuotaPage-Ci5O4BnH.js.gz +0 -0
  568. package/dist/dashboard/assets/SettingsPage-CDZjTt87.js +0 -1
  569. package/dist/dashboard/assets/SettingsPage-CDZjTt87.js.gz +0 -0
  570. package/dist/dashboard/assets/SetupPage-BQS0Rx_u.js.gz +0 -0
  571. package/dist/dashboard/assets/SortableList-BAL5npBC.js +0 -1
  572. package/dist/dashboard/assets/SortableList-BAL5npBC.js.gz +0 -0
  573. package/dist/dashboard/assets/StudioPage-TP7voAQ0.js.gz +0 -0
  574. package/dist/dashboard/assets/UsagePage-Cj_fEzhg.js +0 -56
  575. package/dist/dashboard/assets/UsagePage-Cj_fEzhg.js.gz +0 -0
  576. package/dist/dashboard/assets/api-keys-MbI7e7rt.js +0 -1
  577. package/dist/dashboard/assets/api-keys-MbI7e7rt.js.gz +0 -0
  578. package/dist/dashboard/assets/app-B2vyPoII.css +0 -2
  579. package/dist/dashboard/assets/app-B2vyPoII.css.gz +0 -0
  580. package/dist/dashboard/assets/assert-C0YYuZJ4.js.gz +0 -0
  581. package/dist/dashboard/assets/badge-B_DMpMJN.js.gz +0 -0
  582. package/dist/dashboard/assets/banner-DMrMPmth.js.gz +0 -0
  583. package/dist/dashboard/assets/card-Be5Uzamx.js.gz +0 -0
  584. package/dist/dashboard/assets/cli-tools-muFRphDu.js.gz +0 -0
  585. package/dist/dashboard/assets/clipboard-button-DOfrVmsi.js.gz +0 -0
  586. package/dist/dashboard/assets/dialog-C9omdjQt.js.gz +0 -0
  587. package/dist/dashboard/assets/drawer-Bm8WKaGY.js.gz +0 -0
  588. package/dist/dashboard/assets/entry-DS90gUks.js +0 -1
  589. package/dist/dashboard/assets/entry-DS90gUks.js.gz +0 -0
  590. package/dist/dashboard/assets/entry-j40LJotO.js +0 -4
  591. package/dist/dashboard/assets/entry-j40LJotO.js.gz +0 -0
  592. package/dist/dashboard/assets/entry-q5HwZ8nN.js.gz +0 -0
  593. package/dist/dashboard/assets/formatters-BTScrQpe.js.gz +0 -0
  594. package/dist/dashboard/assets/github-badge-Orjuzemr.js.gz +0 -0
  595. package/dist/dashboard/assets/index-cPrX5vn6.js.gz +0 -0
  596. package/dist/dashboard/assets/inline-Dvux8w-0.js.gz +0 -0
  597. package/dist/dashboard/assets/input-CU1Rgf3p.js.gz +0 -0
  598. package/dist/dashboard/assets/list-row-CzFvogbj.js.gz +0 -0
  599. package/dist/dashboard/assets/live-KaG15HLQ.js +0 -1
  600. package/dist/dashboard/assets/live-KaG15HLQ.js.gz +0 -0
  601. package/dist/dashboard/assets/minimize-2-D-MN8W8a.js +0 -1
  602. package/dist/dashboard/assets/minimize-2-D-MN8W8a.js.gz +0 -0
  603. package/dist/dashboard/assets/mutation-JJGQm8TD.js.gz +0 -0
  604. package/dist/dashboard/assets/numbers-BNDZnsUE.js +0 -2
  605. package/dist/dashboard/assets/numbers-BNDZnsUE.js.gz +0 -0
  606. package/dist/dashboard/assets/query-keys-CLaNz8Xz.js.gz +0 -0
  607. package/dist/dashboard/assets/quota-DZGyp0N5.js +0 -1
  608. package/dist/dashboard/assets/quota-DZGyp0N5.js.gz +0 -0
  609. package/dist/dashboard/assets/routing-BEH32QOC.js.gz +0 -0
  610. package/dist/dashboard/assets/select-B3iTetHN.js.gz +0 -0
  611. package/dist/dashboard/assets/settings-Dt3Eqbnq.js +0 -1
  612. package/dist/dashboard/assets/settings-Dt3Eqbnq.js.gz +0 -0
  613. package/dist/dashboard/assets/stack-KrOrm7JZ.js.gz +0 -0
  614. package/dist/dashboard/assets/state-DLS7IDMy.js.gz +0 -0
  615. package/dist/dashboard/assets/stream-retry-5QnVuSSo.js +0 -1
  616. package/dist/dashboard/assets/stream-retry-5QnVuSSo.js.gz +0 -0
  617. package/dist/dashboard/assets/switch-42ItvbAs.js.gz +0 -0
  618. package/dist/dashboard/assets/useMutation-Dmnfh1Ue.js.gz +0 -0
  619. package/src/network/response-headers.ts +0 -39
  620. package/src/protocol/response/dsml.ts +0 -38
  621. package/src/providers/integrations/xiaomi-mimo/think-stream.ts +0 -229
  622. /package/dist/dashboard/assets/{app-wvwCi4Eh.js → app-Bgd3zANQ.js} +0 -0
  623. /package/dist/dashboard/assets/{app-wvwCi4Eh.js.gz → app-Bgd3zANQ.js.gz} +0 -0
  624. /package/dist/dashboard/assets/brand/{og_bansos.webp → og_bansos_initial.webp} +0 -0
  625. /package/dist/dashboard/assets/brand/{og_image.webp → og_image_initial.webp} +0 -0
@@ -0,0 +1,308 @@
1
+ /**
2
+ * Background execution for the manual daily-growth batch (`POST /quota/daily-growth`).
3
+ *
4
+ * The route used to run the whole batch synchronously — chunked 3-parallel,
5
+ * up to two upstream HTTP calls per account, no global timeout — so a large
6
+ * roster held the console request open and blocked the event loop. This module
7
+ * moves the orchestration to an in-process background job: the route enqueues
8
+ * and returns `{ jobId }` immediately, and a status route reads the job's
9
+ * progress.
10
+ *
11
+ * The per-account runner (report then claim, egress rotation, per-leg timeout)
12
+ * is injected by the route — the job owns only the orchestration: the
13
+ * bounded-concurrency worker loop, the single-result aggregation, and the
14
+ * deduplication guards. This keeps the module free of the route's imports and
15
+ * avoids a cycle between the two quota modules.
16
+ *
17
+ * One job per (tenant, provider) key is allowed at a time. The key is the
18
+ * double-claim guard between two manual triggers: enqueuing again while a job
19
+ * for the same tenant+provider is still queued/running returns the existing
20
+ * job id instead of stamping the same accounts twice. In parallel with the
21
+ * daily sweep, the ledger is the guard — the runner stamps
22
+ * `markCheckinAttempted` after a successful claim, and this runner skips
23
+ * accounts the sweep (or an earlier run) already settled today, so the same
24
+ * account is never claimed twice in one window.
25
+ *
26
+ * The registry is intentionally process-local: the manual trigger is an
27
+ * operator action on a single gateway, so a cross-process queue would buy
28
+ * nothing here.
29
+ */
30
+ import { randomUUID } from "node:crypto";
31
+ import type { CartethyiaDatabase } from "../../persistence/db-handle";
32
+ import type { RedisClient } from "../../persistence/redis";
33
+ import { checkinAttemptedToday } from "../../workers/daily-checkin";
34
+ import type { ValidatedFetch } from "../../network/outbound-fetch";
35
+
36
+ /** Lifecycle states of a daily-growth job. */
37
+ export type DailyGrowthJobState = "queued" | "running" | "done" | "failed";
38
+
39
+ /** One account the job failed to settle, with the reason. */
40
+ export interface DailyGrowthJobError {
41
+ readonly accountId: string;
42
+ readonly error: string;
43
+ }
44
+
45
+ /** Aggregate outcome of one job. */
46
+ export interface DailyGrowthJobResult {
47
+ /** Accounts the job actually attempted (excludes skipped/already-settled). */
48
+ readonly total: number;
49
+ readonly succeeded: number;
50
+ readonly failed: number;
51
+ readonly errors: readonly DailyGrowthJobError[];
52
+ }
53
+
54
+ /** Public view of a job, as the status route returns it. */
55
+ export interface DailyGrowthJobView {
56
+ readonly jobId: string;
57
+ readonly state: DailyGrowthJobState;
58
+ readonly providerId: string;
59
+ readonly createdAt: string;
60
+ readonly finishedAt: string | null;
61
+ readonly total: number;
62
+ readonly succeeded: number;
63
+ readonly failed: number;
64
+ readonly errors: readonly DailyGrowthJobError[];
65
+ }
66
+
67
+ /** One account the job will run the daily-growth pass over. */
68
+ export interface DailyGrowthJobAccount {
69
+ readonly id: string;
70
+ readonly tenantId: string | null;
71
+ readonly label: string;
72
+ }
73
+
74
+ /** Outcome the route's per-account runner reports. */
75
+ export interface DailyGrowthAccountOutcome {
76
+ readonly ok: boolean;
77
+ /** Present when `ok` is false: the reason the account was not settled. */
78
+ readonly error?: string;
79
+ }
80
+
81
+ /** Everything the background runner needs to execute one job. */
82
+ export interface DailyGrowthJobDeps {
83
+ readonly db: CartethyiaDatabase;
84
+ readonly redis: RedisClient | undefined;
85
+ /** Runs one account's daily-growth pass (report then claim). */
86
+ readonly runAccount: (account: DailyGrowthJobAccount) => Promise<DailyGrowthAccountOutcome>;
87
+ /** Invalidated once when the job finishes, so the page re-reads the ledger. */
88
+ readonly snapshotInvalidator?: { invalidate(): Promise<number> };
89
+ }
90
+
91
+ /** Bound on accounts processed at once. Mirrors the old synchronous chunk size. */
92
+ const DAILY_GROWTH_CONCURRENCY = 3;
93
+ /**
94
+ * Per-account leg budget. Each individual upstream call already carries its own
95
+ * 15s abort timeout; this bounds the whole leg (credential resolve + report +
96
+ * check-in) so a single stuck account cannot hold a job worker open forever.
97
+ */
98
+ export const DAILY_GROWTH_LEG_TIMEOUT_MS = 60_000;
99
+
100
+ /**
101
+ * Wraps a fetcher so every request also carries `budget`, even when the callee
102
+ * sets its own per-request timeout. The buddy/qoder billing helpers pass an
103
+ * `AbortSignal.timeout(...)` of their own, which would override a plain
104
+ * `signalFetch`-style injection; combining signals intersects both bounds.
105
+ */
106
+ export function withLegBudget(
107
+ fetcher: ValidatedFetch | typeof fetch,
108
+ budget: AbortSignal,
109
+ ): typeof fetch {
110
+ const wrapped = (
111
+ input: Parameters<typeof fetch>[0],
112
+ init?: Parameters<typeof fetch>[1],
113
+ ): Promise<Response> =>
114
+ fetcher(input, {
115
+ ...init,
116
+ signal: init?.signal ? AbortSignal.any([budget, init.signal]) : budget,
117
+ });
118
+ return Object.assign(wrapped, {
119
+ preconnect: (fetcher as typeof fetch).preconnect,
120
+ });
121
+ }
122
+
123
+ /** Registry cap; oldest finished jobs are evicted so the map cannot grow unbounded. */
124
+ const DAILY_GROWTH_JOB_MAX = 64;
125
+
126
+ interface DailyGrowthJobRecord {
127
+ readonly id: string;
128
+ readonly key: string;
129
+ readonly tenantId: string;
130
+ readonly providerId: string;
131
+ readonly createdAt: number;
132
+ state: DailyGrowthJobState;
133
+ finishedAt: number | null;
134
+ readonly accounts: readonly DailyGrowthJobAccount[];
135
+ result: DailyGrowthJobResult;
136
+ }
137
+
138
+ const jobs = new Map<string, DailyGrowthJobRecord>();
139
+ /** tenant+provider key → job id; the double-trigger guard. */
140
+ const jobsByKey = new Map<string, string>();
141
+ /**
142
+ * Accounts currently being worked by any job. Global accounts (`tenant_id IS
143
+ * NULL`) are visible to every tenant, so two tenants can enqueue jobs over the
144
+ * same global account; this set keeps them from stamping it twice in the same
145
+ * window. Tenant-owned accounts cannot collide this way (they are unique to
146
+ * their tenant), but the set is uniform and cheap.
147
+ */
148
+ const inFlightAccounts = new Set<string>();
149
+
150
+ function jobView(job: DailyGrowthJobRecord): DailyGrowthJobView {
151
+ return {
152
+ jobId: job.id,
153
+ state: job.state,
154
+ providerId: job.providerId,
155
+ createdAt: new Date(job.createdAt).toISOString(),
156
+ finishedAt: job.finishedAt === null ? null : new Date(job.finishedAt).toISOString(),
157
+ total: job.result.total,
158
+ succeeded: job.result.succeeded,
159
+ failed: job.result.failed,
160
+ errors: job.result.errors,
161
+ };
162
+ }
163
+
164
+ /** Evicts the oldest finished jobs once the registry exceeds its cap. */
165
+ function evictFinishedJobs(): void {
166
+ if (jobs.size <= DAILY_GROWTH_JOB_MAX) return;
167
+ const finished: DailyGrowthJobRecord[] = [];
168
+ for (const job of jobs.values()) {
169
+ if (job.state === "done" || job.state === "failed") finished.push(job);
170
+ }
171
+ finished.sort((left, right) => left.createdAt - right.createdAt);
172
+ let excess = jobs.size - DAILY_GROWTH_JOB_MAX;
173
+ for (const job of finished) {
174
+ if (excess <= 0) break;
175
+ jobs.delete(job.id);
176
+ if (jobsByKey.get(job.key) === job.id) jobsByKey.delete(job.key);
177
+ excess -= 1;
178
+ }
179
+ }
180
+
181
+ /**
182
+ * Runs one job to completion. Never rejects: every failure is collected into
183
+ * the job's result, so the status route always has an answer.
184
+ */
185
+ async function runJob(record: DailyGrowthJobRecord, deps: DailyGrowthJobDeps): Promise<void> {
186
+ record.state = "running";
187
+ const succeeded: string[] = [];
188
+ const errors: DailyGrowthJobError[] = [];
189
+ const attempted: string[] = [];
190
+ let cursor = 0;
191
+ const accounts = record.accounts;
192
+
193
+ const runOne = async (account: DailyGrowthJobAccount): Promise<void> => {
194
+ // Double-claim guard with the daily sweep (and any other job): the ledger
195
+ // is shared, so an account the sweep already settled today is skipped
196
+ // rather than claimed again — the same `markCheckinAttempted` semantics
197
+ // the button uses.
198
+ if (inFlightAccounts.has(account.id)) return;
199
+ try {
200
+ if (await checkinAttemptedToday({ db: deps.db, redis: deps.redis, accountId: account.id })) {
201
+ return;
202
+ }
203
+ } catch {
204
+ // An unreadable ledger reads as "not attempted" — the safe direction for
205
+ // a manual trigger, which asserts "try now".
206
+ }
207
+ inFlightAccounts.add(account.id);
208
+ attempted.push(account.id);
209
+ try {
210
+ const outcome = await deps.runAccount(account);
211
+ if (outcome.ok) {
212
+ succeeded.push(account.id);
213
+ } else {
214
+ errors.push({ accountId: account.id, error: outcome.error ?? "Daily growth failed" });
215
+ }
216
+ } finally {
217
+ inFlightAccounts.delete(account.id);
218
+ }
219
+ };
220
+
221
+ await Promise.all(
222
+ Array.from({ length: Math.min(DAILY_GROWTH_CONCURRENCY, accounts.length) }, async () => {
223
+ for (;;) {
224
+ const index = cursor;
225
+ cursor += 1;
226
+ if (index >= accounts.length) return;
227
+ await runOne(accounts[index]!);
228
+ }
229
+ }),
230
+ );
231
+
232
+ record.result = {
233
+ total: attempted.length,
234
+ succeeded: succeeded.length,
235
+ failed: errors.length,
236
+ errors,
237
+ };
238
+ record.finishedAt = Date.now();
239
+ record.state = "done";
240
+ if (deps.snapshotInvalidator) {
241
+ try {
242
+ await deps.snapshotInvalidator.invalidate();
243
+ } catch {
244
+ // Best-effort: a failed snapshot invalidate must not fail the job.
245
+ }
246
+ }
247
+ }
248
+
249
+ /**
250
+ * Enqueues a daily-growth job and returns its id immediately. When a job for
251
+ * the same (tenant, provider) is already queued or running, the existing job
252
+ * id is returned instead — the operator pressed the button twice, and running
253
+ * the same accounts twice would double-claim.
254
+ */
255
+ export function enqueueDailyGrowthJob(args: {
256
+ readonly deps: DailyGrowthJobDeps;
257
+ readonly tenantId: string;
258
+ readonly providerId: string;
259
+ readonly accounts: readonly DailyGrowthJobAccount[];
260
+ }): string {
261
+ const key = `${args.tenantId}:${args.providerId}`;
262
+ const existing = jobsByKey.get(key);
263
+ if (existing !== undefined) {
264
+ const job = jobs.get(existing);
265
+ if (job !== undefined && (job.state === "queued" || job.state === "running")) {
266
+ return job.id;
267
+ }
268
+ jobsByKey.delete(key);
269
+ }
270
+
271
+ const id = randomUUID();
272
+ const record: DailyGrowthJobRecord = {
273
+ id,
274
+ key,
275
+ tenantId: args.tenantId,
276
+ providerId: args.providerId,
277
+ createdAt: Date.now(),
278
+ state: "queued",
279
+ finishedAt: null,
280
+ accounts: args.accounts,
281
+ result: { total: 0, succeeded: 0, failed: 0, errors: [] },
282
+ };
283
+ jobs.set(id, record);
284
+ jobsByKey.set(key, id);
285
+ evictFinishedJobs();
286
+ void runJob(record, args.deps).catch(() => {
287
+ record.state = "failed";
288
+ record.finishedAt = Date.now();
289
+ });
290
+ return id;
291
+ }
292
+
293
+ /**
294
+ * Reads one job's status. Scoped to the requesting tenant: a job enqueued by
295
+ * another tenant is not visible, so one tenant cannot probe another's work.
296
+ */
297
+ export function getDailyGrowthJob(jobId: string, tenantId: string): DailyGrowthJobView | null {
298
+ const job = jobs.get(jobId);
299
+ if (job === undefined || job.tenantId !== tenantId) return null;
300
+ return jobView(job);
301
+ }
302
+
303
+ /** Test seam: clears the registry so suites do not leak state across runs. */
304
+ export function clearDailyGrowthJobsForTests(): void {
305
+ jobs.clear();
306
+ jobsByKey.clear();
307
+ inFlightAccounts.clear();
308
+ }
@@ -20,7 +20,7 @@ import { ConsoleDomainError, requireGlobalAdmin, requireTenantScope } from "../s
20
20
  import type { ConsoleAccessResolver } from "../auth/access";
21
21
 
22
22
  /** True when `token` looks like a plausible machine token. */
23
- export function isValidNativeIdentityToken(token: string): boolean {
23
+ function isValidNativeIdentityToken(token: string): boolean {
24
24
  return typeof token === "string" && token.length >= 8 && token.length <= 512;
25
25
  }
26
26
 
@@ -120,7 +120,7 @@ export function createNativeIdentityOps(
120
120
  * (`tenant_id IS NULL`) need platform admin; tenant-owned accounts need the
121
121
  * tenant scope for the owning tenant.
122
122
  */
123
- export interface NativeIdentityScope {
123
+ interface NativeIdentityScope {
124
124
  readonly tenantId: string | null;
125
125
  }
126
126
 
@@ -11,7 +11,7 @@ import {
11
11
  } from "../../providers/quota/refresh";
12
12
 
13
13
  /** Shape returned for quota data on dashboard account responses. */
14
- export interface QuotaRefreshView {
14
+ interface QuotaRefreshView {
15
15
  readonly source: string;
16
16
  readonly status: "ready" | "error" | "refreshing";
17
17
  readonly plan: string | null;
@@ -14,7 +14,7 @@ import { consoleErrorHandler, requireTenantScope } from "../shared/errors";
14
14
  import { REMOTE_SUPPORT_TOOLS } from "./contracts";
15
15
  import type { RemoteSupportService } from "./service";
16
16
 
17
- export interface RemoteSupportRoutesConfig {
17
+ interface RemoteSupportRoutesConfig {
18
18
  readonly accessResolver: ConsoleAccessResolver;
19
19
  readonly service: RemoteSupportService;
20
20
  }
@@ -33,7 +33,7 @@ const URL_POLL_MS = 500;
33
33
  /** Cached install detection so we don't spawn `where`/`which` on every poll. */
34
34
  const installedCache = new Map<RemoteSupportTool, boolean>();
35
35
 
36
- export interface RemoteSupportServiceConfig {
36
+ interface RemoteSupportServiceConfig {
37
37
  /** Defaults to the gateway's configured port. */
38
38
  readonly port: number;
39
39
  /** Overridable for tests. Defaults to a subdir of the user config dir. */
@@ -245,7 +245,7 @@ function tailscaleDnsName(): string | null {
245
245
  * nothing about this: stopping the tunnel leaves the node up and empties the
246
246
  * status, which is why an online node alone was read as "still active".
247
247
  */
248
- export function tailscaleServing(): boolean {
248
+ function tailscaleServing(): boolean {
249
249
  const result = spawnSync("tailscale", ["serve", "status", "--json"], { encoding: "utf8", windowsHide: true });
250
250
  if (result.status !== 0) return false;
251
251
  let parsed: unknown;
@@ -102,7 +102,7 @@ export interface ModelRoutingStore {
102
102
 
103
103
  /** Structurally satisfied by `InMemoryRouteSnapshotService` as-is — no import
104
104
  * of routing internals into the console domain layer. */
105
- export interface ModelRoutingSnapshotInvalidator {
105
+ interface ModelRoutingSnapshotInvalidator {
106
106
  invalidate(): Promise<number>;
107
107
  }
108
108
 
@@ -1,5 +1,5 @@
1
1
  // Model routing control-plane HTTP routes. The only module that touches Elysia.
2
- import { Elysia, t } from "elysia";
2
+ import { Elysia, t, type AnyElysia } from "elysia";
3
3
  import { errorResponse } from "../../shared/errors";
4
4
  import { literalUnion } from "../../shared/elysia-schema";
5
5
  import { modelComboStrategy, type ComboStrategy } from "../../../persistence/schema";
@@ -41,7 +41,7 @@ const updateComboBody = t.Object({
41
41
  members: t.Optional(t.Array(t.String(), { minItems: 1 })),
42
42
  strategy: t.Optional(comboStrategySchema),
43
43
  });
44
- export function createModelRoutingRoutes(config: ModelRoutingConfig): Elysia {
44
+ export function createModelRoutingRoutes(config: ModelRoutingConfig): AnyElysia {
45
45
  const factory = createModelRoutingOperations(config);
46
46
  return new Elysia({ prefix: "/routing" })
47
47
  .get("/aliases", async ({ request, set }) => {
@@ -132,5 +132,5 @@ export function createModelRoutingRoutes(config: ModelRoutingConfig): Elysia {
132
132
  } catch (e) {
133
133
  return modelRoutingErrorResponse(e, set);
134
134
  }
135
- }) as unknown as Elysia;
135
+ });
136
136
  }
@@ -188,7 +188,7 @@ export function validateTransportConfig(
188
188
  case "http":
189
189
  case "https":
190
190
  if (cfg.timeout !== undefined && typeof cfg.timeout !== "number")
191
- throw new ConsoleDomainError("invalid_config", 400, "HTTP timeout must be a number");
191
+ throw new ConsoleDomainError("invalid_pool", 400, "HTTP timeout must be a number", { field: "timeout" });
192
192
  return;
193
193
  case "socks5":
194
194
  return;
@@ -196,9 +196,10 @@ export function validateTransportConfig(
196
196
  // Compile-time exhaustiveness: new TransportKind variants must add a case.
197
197
  kind satisfies never;
198
198
  throw new ConsoleDomainError(
199
- "invalid_config",
199
+ "invalid_pool",
200
200
  400,
201
201
  `Unsupported transport kind: ${String(kind)}`,
202
+ { field: "kind" },
202
203
  );
203
204
  }
204
205
  }
@@ -222,7 +223,7 @@ export type PoolStatus = (typeof POOL_STATUSES)[number];
222
223
  * stay aligned with the `pool_routing_strategy` enum in `schema.ts`.
223
224
  */
224
225
  export const POOL_ROUTING_STRATEGIES = ["least_loaded", "round_robin", "random"] as const;
225
- export type PoolRoutingStrategyValue = (typeof POOL_ROUTING_STRATEGIES)[number];
226
+ type PoolRoutingStrategyValue = (typeof POOL_ROUTING_STRATEGIES)[number];
226
227
 
227
228
  export interface PoolStrategySetting {
228
229
  strategy: PoolRoutingStrategyValue;
@@ -455,7 +456,7 @@ export const MAX_BATCH_PROBE_TARGETS = 100;
455
456
  */
456
457
  export const PROVIDER_PROBE_BATCH_CONCURRENCY = 4;
457
458
 
458
- export interface NetworkPoolAgentReleaser {
459
+ interface NetworkPoolAgentReleaser {
459
460
  releasePool(poolId: string, tenantId: string): Promise<void>;
460
461
  }
461
462
  export interface NetworkPoolConfig {
@@ -494,10 +495,10 @@ export function validateEndpoint(
494
495
  try {
495
496
  u = new URL(candidate);
496
497
  } catch {
497
- throw new ConsoleDomainError("invalid_endpoint", 400, "Endpoint must be host:port");
498
+ throw new ConsoleDomainError("invalid_pool", 400, "Endpoint must be host:port", { field: "endpoint" });
498
499
  }
499
500
  if (!u.hostname || !u.port)
500
- throw new ConsoleDomainError("invalid_endpoint", 400, "Endpoint must be host:port");
501
+ throw new ConsoleDomainError("invalid_pool", 400, "Endpoint must be host:port", { field: "endpoint" });
501
502
  if (u.hostname === "localhost" || u.hostname.endsWith(".internal"))
502
503
  throw new ConsoleDomainError("ssrf_rejected", 400, "Private endpoint rejected");
503
504
  if (isIP(u.hostname) !== 0 && !isAddressAllowed(u.hostname, policy))
@@ -508,10 +509,10 @@ export function validateEndpoint(
508
509
  try {
509
510
  u = new URL(endpoint);
510
511
  } catch {
511
- throw new ConsoleDomainError("invalid_endpoint", 400, "Endpoint must be a valid http(s) URL");
512
+ throw new ConsoleDomainError("invalid_pool", 400, "Endpoint must be a valid http(s) URL", { field: "endpoint" });
512
513
  }
513
514
  if (!["http:", "https:"].includes(u.protocol)) {
514
- throw new ConsoleDomainError("invalid_endpoint", 400, "Endpoint must be a valid http(s) URL");
515
+ throw new ConsoleDomainError("invalid_pool", 400, "Endpoint must be a valid http(s) URL", { field: "endpoint" });
515
516
  }
516
517
  if (u.hostname === "localhost" || u.hostname.endsWith(".internal")) {
517
518
  throw new ConsoleDomainError("ssrf_rejected", 400, "Private endpoint rejected");
@@ -529,9 +530,10 @@ export function validatePoolLimits(maxInflight?: number, weight?: number): void
529
530
  (!Number.isInteger(maxInflight) || maxInflight < 1 || maxInflight > MAX_POOL_INFLIGHT)
530
531
  ) {
531
532
  throw new ConsoleDomainError(
532
- "invalid_pool_limits",
533
+ "invalid_pool",
533
534
  400,
534
535
  "maxInflight must be an integer between 1 and 1000000",
536
+ { field: "maxInflight" },
535
537
  );
536
538
  }
537
539
  if (
@@ -539,9 +541,10 @@ export function validatePoolLimits(maxInflight?: number, weight?: number): void
539
541
  (!Number.isInteger(weight) || weight < 1 || weight > MAX_POOL_WEIGHT)
540
542
  ) {
541
543
  throw new ConsoleDomainError(
542
- "invalid_pool_limits",
544
+ "invalid_pool",
543
545
  400,
544
546
  "weight must be an integer between 1 and 1000",
547
+ { field: "weight" },
545
548
  );
546
549
  }
547
550
  }
@@ -18,7 +18,7 @@
18
18
  import { POOL_KINDS, POOL_STATUSES } from "./contracts";
19
19
 
20
20
  /** One row that parsed, ready to become a pool. */
21
- export interface ParsedProxyRow {
21
+ interface ParsedProxyRow {
22
22
  readonly kind: (typeof POOL_KINDS)[number];
23
23
  readonly endpoint: string;
24
24
  readonly credential: string | null;
@@ -26,12 +26,12 @@ export interface ParsedProxyRow {
26
26
  }
27
27
 
28
28
  /** One row that did not parse, with the reason. */
29
- export interface SkippedProxyRow {
29
+ interface SkippedProxyRow {
30
30
  readonly line: number;
31
31
  readonly reason: string;
32
32
  }
33
33
 
34
- export interface ParsedProxyFile {
34
+ interface ParsedProxyFile {
35
35
  readonly rows: readonly ParsedProxyRow[];
36
36
  readonly skipped: readonly SkippedProxyRow[];
37
37
  }
@@ -141,9 +141,10 @@ export function createNetworkPoolOperations(config: NetworkPoolConfig) {
141
141
  const rotateCount = request.rotateCount ?? current.rotateCount;
142
142
  if (!Number.isInteger(rotateCount) || rotateCount < 1 || rotateCount > 1000) {
143
143
  throw new ConsoleDomainError(
144
- "invalid_pool_limits",
144
+ "invalid_pool",
145
145
  400,
146
146
  "rotateCount must be an integer between 1 and 1000",
147
+ { field: "rotateCount" },
147
148
  );
148
149
  }
149
150
  const saved = await config.store.setStrategy(a.tenantId, { strategy, rotateCount });
@@ -447,6 +448,12 @@ export function createNetworkPoolOperations(config: NetworkPoolConfig) {
447
448
  const a = requireTenantScope(access, "dashboard:write");
448
449
  const recovered = await config.store.recover(a.tenantId, poolId);
449
450
  if (!recovered) throw new ConsoleDomainError("pool_not_found", 404, `Pool ${poolId} not found`);
451
+ // Recover is the operator's "make this pool healthy again": besides
452
+ // resetting the pool-level state, drop every provider-rate-limit marker
453
+ // (volatile and durable) so a just-cleared pool is not still excluded
454
+ // by a stale 429 flag.
455
+ await config.poolSelector?.clearPoolCooldowns(poolId).catch(() => undefined);
456
+ await config.store.dropAllProviderCooldownMarkers?.(a.tenantId, poolId);
450
457
  await config.poolAgentReleaser?.releasePool(poolId, a.tenantId);
451
458
  await config.auditSink?.record({
452
459
  access: a,
@@ -482,7 +489,7 @@ export function createNetworkPoolOperations(config: NetworkPoolConfig) {
482
489
  ): Promise<PoolSpeedTestResult> {
483
490
  const a = requireTenantScope(access, "dashboard:read");
484
491
  if (!config.store.speedTest) {
485
- throw new ConsoleDomainError("not_supported", 400, "Speed tests are unavailable");
492
+ throw new ConsoleDomainError("capability_unsupported", 400, "Speed tests are unavailable");
486
493
  }
487
494
  if (!Number.isInteger(bytes) || bytes < SPEED_TEST_MIN_BYTES || bytes > SPEED_TEST_MAX_BYTES) {
488
495
  throw new ConsoleDomainError(
@@ -507,7 +514,7 @@ export function createNetworkPoolOperations(config: NetworkPoolConfig) {
507
514
  ): Promise<PoolProviderProbeResult> {
508
515
  const a = requireTenantScope(access, "dashboard:read");
509
516
  if (!config.store.providerProbe) {
510
- throw new ConsoleDomainError("not_supported", 400, "Provider probing is unavailable");
517
+ throw new ConsoleDomainError("capability_unsupported", 400, "Provider probing is unavailable");
511
518
  }
512
519
  const result = await config.store.providerProbe(a.tenantId, poolId, providerId);
513
520
  if (!config.poolSelector) return result;
@@ -596,7 +603,7 @@ export function createNetworkPoolOperations(config: NetworkPoolConfig) {
596
603
  );
597
604
  }
598
605
  if (!config.store.probeAdHocProvider) {
599
- throw new ConsoleDomainError("not_supported", 400, "Provider probing is unavailable");
606
+ throw new ConsoleDomainError("capability_unsupported", 400, "Provider probing is unavailable");
600
607
  }
601
608
  const probe = config.store.probeAdHocProvider.bind(config.store);
602
609
  const results: PoolBatchProviderProbeResult[] = new Array(requests.length);
@@ -632,7 +639,7 @@ export function createNetworkPoolOperations(config: NetworkPoolConfig) {
632
639
  if (config.store.probeAdHoc) {
633
640
  return config.store.probeAdHoc(a.tenantId, request);
634
641
  }
635
- throw new ConsoleDomainError("not_supported", 400, "Ad-hoc pool probing is unavailable");
642
+ throw new ConsoleDomainError("capability_unsupported", 400, "Ad-hoc pool probing is unavailable");
636
643
  },
637
644
  /**
638
645
  * Probes many unsaved pool definitions in one call.
@@ -656,7 +663,7 @@ export function createNetworkPoolOperations(config: NetworkPoolConfig) {
656
663
  );
657
664
  }
658
665
  if (!config.store.probeAdHoc) {
659
- throw new ConsoleDomainError("not_supported", 400, "Ad-hoc pool probing is unavailable");
666
+ throw new ConsoleDomainError("capability_unsupported", 400, "Ad-hoc pool probing is unavailable");
660
667
  }
661
668
  const probe = config.store.probeAdHoc.bind(config.store);
662
669
  const results: PoolBatchProbeResult[] = new Array(requests.length);
@@ -700,7 +707,7 @@ export function createNetworkPoolOperations(config: NetworkPoolConfig) {
700
707
  ): Promise<NetworkPoolResponse & { relayUrl: string }> {
701
708
  const a = requireTenantScope(access, "dashboard:write");
702
709
  if (!config.relayFetch)
703
- throw new ConsoleDomainError("not_supported", 503, "Relay deployment is unavailable");
710
+ throw new ConsoleDomainError("capability_unsupported", 503, "Relay deployment is unavailable");
704
711
  if (!isRelayTarget(request.target))
705
712
  throw new ConsoleDomainError("invalid_request", 422, `Unsupported relay target: ${String(request.target)}`);
706
713
  const result = await deployRelayWorker(config.relayFetch, request);
@@ -15,8 +15,16 @@
15
15
  * the address works (`allowed`, `throttled`) or says nothing about it
16
16
  * (`unreachable`), and treating "says nothing" as "refused" would drop a usable
17
17
  * pool for good.
18
+ *
19
+ * The 403-free-tier and 429 verdicts are the probe's reading of the same facts
20
+ * `classifyEgressFailure` (in `network/rate-limit-verdict`) reads on the
21
+ * dispatch path — for the probe they arrive as an HTTP status + body instead of
22
+ * a `GatewayError`, so the shared free-tier regex is borrowed from there to
23
+ * keep the two classifiers' boundaries honest.
18
24
  */
19
25
 
26
+ import { FREE_TIER_REFUSAL } from "../../../network/rate-limit-verdict";
27
+
20
28
  export type ProbeVerdict = "allowed" | "blocked" | "throttled" | "unreachable";
21
29
 
22
30
  export interface ProbeAnswer {
@@ -25,9 +33,6 @@ export interface ProbeAnswer {
25
33
  readonly reason?: string;
26
34
  }
27
35
 
28
- /** The upstream's own wording for "this address is retired". */
29
- const FREE_TIER_REFUSAL = /freetier|free tier can only be used/i;
30
-
31
36
  /**
32
37
  * Classifies one upstream response.
33
38
  *