cartethyia 2.7.9 → 2.8.1

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 (496) hide show
  1. package/.env.example +6 -5
  2. package/dist/dashboard/assets/{BannedPage-BxoG1MrL.js → BannedPage-Dq530awD.js} +1 -1
  3. package/dist/dashboard/assets/BannedPage-Dq530awD.js.gz +0 -0
  4. package/dist/dashboard/assets/{CliToolDetailPage-ixwgKk53.js → CliToolDetailPage-DZzaS-7S.js} +1 -1
  5. package/dist/dashboard/assets/CliToolDetailPage-DZzaS-7S.js.gz +0 -0
  6. package/dist/dashboard/assets/{CliToolsPage-BmttNV3U.js → CliToolsPage-DaZZ3cqP.js} +1 -1
  7. package/dist/dashboard/assets/CliToolsPage-DaZZ3cqP.js.gz +0 -0
  8. package/dist/dashboard/assets/{CombosPage-BE3qwwfC.js → CombosPage-BlC_xdkX.js} +1 -1
  9. package/dist/dashboard/assets/CombosPage-BlC_xdkX.js.gz +0 -0
  10. package/dist/dashboard/assets/{ConsoleLogPage-CRZdmMIX.js → ConsoleLogPage-D-rqeVtG.js} +1 -1
  11. package/dist/dashboard/assets/ConsoleLogPage-D-rqeVtG.js.gz +0 -0
  12. package/dist/dashboard/assets/{CustomizationPage-Dv_FJ6Er.js → CustomizationPage-7xT5yg2E.js} +1 -1
  13. package/dist/dashboard/assets/CustomizationPage-7xT5yg2E.js.gz +0 -0
  14. package/dist/dashboard/assets/HealthEventsModal-Clq8E9cW.js +1 -0
  15. package/dist/dashboard/assets/HealthEventsModal-Clq8E9cW.js.gz +0 -0
  16. package/dist/dashboard/assets/{InjectSystemPromptPanel-BOoO8rpz.js → InjectSystemPromptPanel-6Teoq9Se.js} +2 -2
  17. package/dist/dashboard/assets/InjectSystemPromptPanel-6Teoq9Se.js.gz +0 -0
  18. package/dist/dashboard/assets/{LoginPage-ChJuNNVN.js → LoginPage-CZuI0MAR.js} +1 -1
  19. package/dist/dashboard/assets/LoginPage-CZuI0MAR.js.gz +0 -0
  20. package/dist/dashboard/assets/{ModelPicker-BZAJtPg1.js → ModelPicker-C1hNTlkr.js} +1 -1
  21. package/dist/dashboard/assets/ModelPicker-C1hNTlkr.js.gz +0 -0
  22. package/dist/dashboard/assets/{OverviewPage-BE40s3u5.js → OverviewPage-0yfhSI1C.js} +1 -1
  23. package/dist/dashboard/assets/OverviewPage-0yfhSI1C.js.gz +0 -0
  24. package/dist/dashboard/assets/ProviderDetailPage-xqTxdQ4c.js +8 -0
  25. package/dist/dashboard/assets/ProviderDetailPage-xqTxdQ4c.js.gz +0 -0
  26. package/dist/dashboard/assets/{ProviderIcon-CQ6bPUf_.js → ProviderIcon-Ddhm1InL.js} +1 -1
  27. package/dist/dashboard/assets/ProviderIcon-Ddhm1InL.js.gz +0 -0
  28. package/dist/dashboard/assets/ProvidersPage-C3fYuu6i.js +1 -0
  29. package/dist/dashboard/assets/ProvidersPage-C3fYuu6i.js.gz +0 -0
  30. package/dist/dashboard/assets/ProxyPage-CHtJtZ3u.js +9 -0
  31. package/dist/dashboard/assets/ProxyPage-CHtJtZ3u.js.gz +0 -0
  32. package/dist/dashboard/assets/QuotaPage-D1RV_2BX.js +1 -0
  33. package/dist/dashboard/assets/QuotaPage-D1RV_2BX.js.gz +0 -0
  34. package/dist/dashboard/assets/{SettingsPage-C5HPWqEv.js → SettingsPage-BzZAvYSX.js} +1 -1
  35. package/dist/dashboard/assets/SettingsPage-BzZAvYSX.js.gz +0 -0
  36. package/dist/dashboard/assets/{SetupPage-CFNcATJJ.js → SetupPage--s-EfEIL.js} +1 -1
  37. package/dist/dashboard/assets/SetupPage--s-EfEIL.js.gz +0 -0
  38. package/dist/dashboard/assets/{StudioPage-YoD4tY5l.js → StudioPage-BjXFGSK4.js} +1 -1
  39. package/dist/dashboard/assets/StudioPage-BjXFGSK4.js.gz +0 -0
  40. package/dist/dashboard/assets/{UsagePage-DxjaAhb_.js → UsagePage-CP8F8BN7.js} +1 -1
  41. package/dist/dashboard/assets/UsagePage-CP8F8BN7.js.gz +0 -0
  42. package/dist/dashboard/assets/{api-keys-BTlkq_Uk.js → api-keys-2hc12Uz2.js} +1 -1
  43. package/dist/dashboard/assets/api-keys-2hc12Uz2.js.gz +0 -0
  44. package/dist/dashboard/assets/app-DGXbNfAR.css +2 -0
  45. package/dist/dashboard/assets/app-DGXbNfAR.css.gz +0 -0
  46. package/dist/dashboard/assets/assert-BMcj1xZE.js +1 -0
  47. package/dist/dashboard/assets/assert-BMcj1xZE.js.gz +0 -0
  48. package/dist/dashboard/assets/{badge-DdbT_7Am.js → badge-DkRUuVfz.js} +1 -1
  49. package/dist/dashboard/assets/badge-DkRUuVfz.js.gz +0 -0
  50. package/dist/dashboard/assets/{banner-CsGN-nbm.js → banner-CDjW841Q.js} +1 -1
  51. package/dist/dashboard/assets/banner-CDjW841Q.js.gz +0 -0
  52. package/dist/dashboard/assets/{card-mgZ_TLLZ.js → card-Bto5UlL9.js} +1 -1
  53. package/dist/dashboard/assets/card-Bto5UlL9.js.gz +0 -0
  54. package/dist/dashboard/assets/{cli-tools-A1-v8-03.js → cli-tools-DaH6g9hL.js} +1 -1
  55. package/dist/dashboard/assets/cli-tools-DaH6g9hL.js.gz +0 -0
  56. package/dist/dashboard/assets/{clipboard-button-CJ4Zty89.js → clipboard-button-BYKBCzjk.js} +1 -1
  57. package/dist/dashboard/assets/clipboard-button-BYKBCzjk.js.gz +0 -0
  58. package/dist/dashboard/assets/{dialog-B5L2jPQ_.js → dialog-DL0Zftwv.js} +1 -1
  59. package/dist/dashboard/assets/dialog-DL0Zftwv.js.gz +0 -0
  60. package/dist/dashboard/assets/{drawer-DkjUouAj.js → drawer-Dpx_UXaS.js} +1 -1
  61. package/dist/dashboard/assets/drawer-Dpx_UXaS.js.gz +0 -0
  62. package/dist/dashboard/assets/{entry-B-LVLV6o.js → entry-BV7-1NoM.js} +1 -1
  63. package/dist/dashboard/assets/entry-BV7-1NoM.js.gz +0 -0
  64. package/dist/dashboard/assets/{entry-CAhdYl3B.js → entry-CGvdNU6r.js} +1 -1
  65. package/dist/dashboard/assets/entry-CGvdNU6r.js.gz +0 -0
  66. package/dist/dashboard/assets/entry-DScKYvZj.js +4 -0
  67. package/dist/dashboard/assets/entry-DScKYvZj.js.gz +0 -0
  68. package/dist/dashboard/assets/{formatters-CrsdLVsn.js → formatters-BS3DZlZj.js} +1 -1
  69. package/dist/dashboard/assets/formatters-BS3DZlZj.js.gz +0 -0
  70. package/dist/dashboard/assets/{index-BU64Lqvp.js → index-SQnvvTWX.js} +2 -2
  71. package/dist/dashboard/assets/index-SQnvvTWX.js.gz +0 -0
  72. package/dist/dashboard/assets/{inline-B2aDLyyg.js → inline-Dl-cuLK0.js} +1 -1
  73. package/dist/dashboard/assets/inline-Dl-cuLK0.js.gz +0 -0
  74. package/dist/dashboard/assets/{input-BCVkdR9d.js → input-Bfx0EzRc.js} +1 -1
  75. package/dist/dashboard/assets/input-Bfx0EzRc.js.gz +0 -0
  76. package/dist/dashboard/assets/{layout-DNcAdMK-.js → layout-BxhbQ_JS.js} +1 -1
  77. package/dist/dashboard/assets/layout-BxhbQ_JS.js.gz +0 -0
  78. package/dist/dashboard/assets/{list-row-DgrIH0hw.js → list-row-D49Y4ptn.js} +1 -1
  79. package/dist/dashboard/assets/list-row-D49Y4ptn.js.gz +0 -0
  80. package/dist/dashboard/assets/{live-Cae01iu8.js → live-CbVTdX9V.js} +1 -1
  81. package/dist/dashboard/assets/live-CbVTdX9V.js.gz +0 -0
  82. package/dist/dashboard/assets/{network-CRib7H_2.js → network-JGvDveof.js} +1 -1
  83. package/dist/dashboard/assets/network-JGvDveof.js.gz +0 -0
  84. package/dist/dashboard/assets/{numbers-BKQE8Xpz.js → numbers-Dw6kiew-.js} +1 -1
  85. package/dist/dashboard/assets/numbers-Dw6kiew-.js.gz +0 -0
  86. package/dist/dashboard/assets/{pagination-ecPvG_8g.js → pagination-iUoI8GeE.js} +1 -1
  87. package/dist/dashboard/assets/pagination-iUoI8GeE.js.gz +0 -0
  88. package/dist/dashboard/assets/providers/sensenova-intl.png +0 -0
  89. package/dist/dashboard/assets/providers/sensenova.png +0 -0
  90. package/dist/dashboard/assets/providers/zro.svg +3 -0
  91. package/dist/dashboard/assets/providers/zro.svg.gz +0 -0
  92. package/dist/dashboard/assets/quota-DiPD6UKH.js +1 -0
  93. package/dist/dashboard/assets/quota-DiPD6UKH.js.gz +0 -0
  94. package/dist/dashboard/assets/{routing-Ckm1UXuC.js → routing-8jZr-3WE.js} +1 -1
  95. package/dist/dashboard/assets/routing-8jZr-3WE.js.gz +0 -0
  96. package/dist/dashboard/assets/{select-ChoFWyKz.js → select-oO1FxMVb.js} +2 -2
  97. package/dist/dashboard/assets/select-oO1FxMVb.js.gz +0 -0
  98. package/dist/dashboard/assets/{settings-CKjFPlKW.js → settings-CLr3kdIx.js} +1 -1
  99. package/dist/dashboard/assets/settings-CLr3kdIx.js.gz +0 -0
  100. package/dist/dashboard/assets/{stack-DWCwU3fh.js → stack-Y3pDteZr.js} +1 -1
  101. package/dist/dashboard/assets/stack-Y3pDteZr.js.gz +0 -0
  102. package/dist/dashboard/assets/{state-Cg3VL5J_.js → state-CArX_Iak.js} +1 -1
  103. package/dist/dashboard/assets/state-CArX_Iak.js.gz +0 -0
  104. package/dist/dashboard/assets/{switch-Db70BMix.js → switch-DwlwmDO-.js} +1 -1
  105. package/dist/dashboard/assets/switch-DwlwmDO-.js.gz +0 -0
  106. package/dist/dashboard/assets/{useMutation-BqdE0LuM.js → useMutation-DJ9GYEgf.js} +1 -1
  107. package/dist/dashboard/assets/useMutation-DJ9GYEgf.js.gz +0 -0
  108. package/dist/dashboard/index.html +2 -2
  109. package/dist/dashboard/index.html.gz +0 -0
  110. package/dist/dashboard/precompressed-assets.json +54 -53
  111. package/migrations/full-postgres/0046_add_accounts_quota_plan_fetched_at.sql +7 -0
  112. package/migrations/full-postgres/0047_add_provider_device_flow_proxy.sql +17 -0
  113. package/migrations/lite-sqlite3/0046_add_accounts_quota_plan_fetched_at.sql +4 -0
  114. package/migrations/lite-sqlite3/0047_add_provider_device_flow_proxy.sql +19 -0
  115. package/package.json +1 -1
  116. package/src/boot/app.ts +11 -14
  117. package/src/boot/dependencies.ts +5 -7
  118. package/src/boot/flush.ts +20 -0
  119. package/src/boot/lifecycle.ts +7 -5
  120. package/src/config/providers.ts +6 -5
  121. package/src/config/routing.ts +26 -0
  122. package/src/config/spec-metadata.ts +72 -0
  123. package/src/config/spec.ts +28 -8
  124. package/src/console/api-keys/contracts.ts +12 -1
  125. package/src/console/api-keys/routes.ts +40 -28
  126. package/src/console/auth/account-reset.ts +7 -9
  127. package/src/console/backup/contracts.ts +4 -0
  128. package/src/console/backup/export-endpoint.ts +7 -5
  129. package/src/console/backup/nine-router.ts +71 -10
  130. package/src/console/backup/service.ts +8 -1
  131. package/src/console/cli-tools/service.ts +6 -12
  132. package/src/console/console-router-guards.ts +23 -16
  133. package/src/console/console-router.ts +1 -1
  134. package/src/console/domain-registration.ts +7 -4
  135. package/src/console/inferhub/client.ts +1 -1
  136. package/src/console/inferhub/normalize.ts +24 -28
  137. package/src/console/inferhub/types.ts +3 -0
  138. package/src/console/ip-bans/routes.ts +7 -13
  139. package/src/console/observability/live.ts +2 -2
  140. package/src/console/observability/store.ts +0 -53
  141. package/src/console/providers/catalog/contracts.ts +9 -3
  142. package/src/console/providers/catalog/model-operations.ts +2 -3
  143. package/src/console/providers/catalog/provider-operations.ts +1 -1
  144. package/src/console/providers/catalog/routes.ts +3 -2
  145. package/src/console/providers/catalog/store.ts +1914 -1902
  146. package/src/console/providers/detail/routes.ts +1 -0
  147. package/src/console/providers/detail/store.ts +4 -0
  148. package/src/console/providers/oauth/callback-listener.ts +30 -11
  149. package/src/console/providers/oauth/device-egress.ts +123 -0
  150. package/src/console/providers/oauth/routes.ts +123 -16
  151. package/src/console/quota/account-quota-global.ts +14 -24
  152. package/src/console/quota/account-quota-shared.ts +49 -0
  153. package/src/console/quota/account-quota-tenant.ts +11 -23
  154. package/src/console/quota/account-test-egress.ts +2 -2
  155. package/src/console/quota/daily-growth-job.ts +1 -1
  156. package/src/console/quota/quota-view.ts +19 -3
  157. package/src/console/routing/pools/contracts.ts +1 -1
  158. package/src/console/routing/pools/operations.ts +32 -47
  159. package/src/console/routing/pools/probe-verdict.ts +2 -2
  160. package/src/console/routing/pools/relay-deploy.ts +1 -1
  161. package/src/console/routing/pools/store.ts +4 -4
  162. package/src/console/shared/errors.ts +33 -6
  163. package/src/main.ts +2 -3
  164. package/src/network/client.ts +67 -0
  165. package/src/network/egress/bidi-session.ts +322 -0
  166. package/src/network/egress/dial.ts +201 -0
  167. package/src/network/egress/factory.ts +191 -0
  168. package/src/network/{outbound-fetch.ts → egress/fetch.ts} +195 -29
  169. package/src/network/egress/tls-profile.ts +123 -0
  170. package/src/network/egress/ws.ts +175 -0
  171. package/src/network/pool/{byte-accounting.ts → accounting.ts} +3 -2
  172. package/src/network/{types.ts → pool/agent-config.ts} +1 -1
  173. package/src/network/pool/agent.ts +2 -2
  174. package/src/network/{pool-cooldown-store.ts → pool/cooldown-store.ts} +95 -20
  175. package/src/network/{pool-health-machine.ts → pool/health.ts} +205 -6
  176. package/src/network/pool/resolver.ts +10 -849
  177. package/src/network/pool/selector.ts +184 -41
  178. package/src/network/ssrf.ts +7 -7
  179. package/src/network/{retry.ts → util/retry.ts} +1 -1
  180. package/src/network/{rate-limit-verdict.ts → verdict.ts} +1 -1
  181. package/src/observability/event-log.ts +7 -361
  182. package/src/observability/performance-metrics.ts +9 -0
  183. package/src/observability/runtime-metrics.ts +14 -9
  184. package/src/observability/telemetry-buffer.ts +16 -26
  185. package/src/observability/telemetry-reconciliation.ts +9 -37
  186. package/src/observability/telemetry-row.ts +125 -0
  187. package/src/persistence/db-mode.ts +4 -1
  188. package/src/persistence/dialect.ts +18 -0
  189. package/src/persistence/migrate.ts +6 -3
  190. package/src/persistence/schema.ts +17 -0
  191. package/src/persistence/ttl-cache.ts +21 -3
  192. package/src/protocol/codecs.ts +59 -2
  193. package/src/protocol/dsml.ts +34 -86
  194. package/src/protocol/markup-stream-parser.ts +195 -0
  195. package/src/protocol/reasoning-text.ts +19 -0
  196. package/src/protocol/request/chat.ts +10 -10
  197. package/src/protocol/request/gemini.ts +8 -2
  198. package/src/protocol/request/messages.ts +34 -22
  199. package/src/protocol/response/chat.ts +12 -20
  200. package/src/protocol/response/messages.ts +34 -7
  201. package/src/protocol/response/omp-tool-call-markup.ts +43 -86
  202. package/src/protocol/response/responses.ts +18 -2
  203. package/src/protocol/stop-reason.ts +23 -1
  204. package/src/protocol/think-split.ts +112 -0
  205. package/src/protocol/translation/dialect/qwen.ts +26 -58
  206. package/src/protocol/translation/quirks.ts +15 -0
  207. package/src/protocol/upstream/send-claude-messages.ts +2 -2
  208. package/src/providers/adapters/compatible-adapter.ts +76 -12
  209. package/src/providers/authentication/oauth-client.ts +40 -6
  210. package/src/providers/authentication/oauth-device-flow.ts +105 -11
  211. package/src/providers/authentication/oauth-flow-store.ts +82 -1
  212. package/src/providers/authentication/oauth-refresh-service.ts +766 -758
  213. package/src/providers/configured-provider.ts +148 -0
  214. package/src/providers/default-registry.ts +137 -36
  215. package/src/providers/discovery/discovery-types.ts +7 -0
  216. package/src/providers/discovery/probe-phases.ts +20 -6
  217. package/src/providers/discovery/probing-service.ts +22 -2
  218. package/src/providers/integrations/alibabacp/alibabacp-models.ts +1 -2
  219. package/src/providers/integrations/{dashscope-models.ts → alibabacp/dashscope-models.ts} +2 -2
  220. package/src/providers/integrations/antigravity/antigravity-protocol.ts +16 -1
  221. package/src/providers/integrations/buddy/account.ts +969 -0
  222. package/src/providers/integrations/buddy/capabilities.ts +12 -12
  223. package/src/providers/integrations/buddy/{buddy-catalog-shared.ts → catalog.ts} +198 -136
  224. package/src/providers/integrations/buddy/{buddy-chat-shared.ts → chat.ts} +176 -20
  225. package/src/providers/integrations/buddy/codebuddy/index.ts +429 -0
  226. package/src/providers/integrations/buddy/codebuddy/shared.ts +41 -26
  227. package/src/providers/integrations/buddy/headers.ts +311 -0
  228. package/src/providers/integrations/buddy/lifecycle.ts +1018 -0
  229. package/src/providers/integrations/buddy/{buddy-media.ts → media.ts} +1 -1
  230. package/src/providers/integrations/claude/claude-quota.ts +14 -17
  231. package/src/providers/integrations/cline/cline-oauth.ts +10 -5
  232. package/src/providers/integrations/cline/cline.ts +5 -6
  233. package/src/providers/integrations/clixad/clixad-oauth.ts +98 -0
  234. package/src/providers/integrations/clixad/clixad-quota.ts +132 -0
  235. package/src/providers/integrations/clixad/provider.ts +145 -0
  236. package/src/providers/integrations/codex/codex-oauth.ts +8 -5
  237. package/src/providers/integrations/commandcode/quota.ts +22 -28
  238. package/src/providers/integrations/cursor/cursor-auth.ts +1 -9
  239. package/src/providers/integrations/cursor/cursor-exec.ts +9 -2
  240. package/src/providers/integrations/cursor/cursor-hardening.ts +279 -0
  241. package/src/providers/integrations/cursor/cursor-http2.ts +605 -6
  242. package/src/providers/integrations/cursor/cursor-request.ts +26 -12
  243. package/src/providers/integrations/cursor/cursor-shared.ts +8 -5
  244. package/src/providers/integrations/cursor/cursor.ts +574 -110
  245. package/src/providers/integrations/cursor/protocol/messages.ts +22 -29
  246. package/src/providers/integrations/cursor/protocol/protobuf-schema.ts +37 -0
  247. package/src/providers/integrations/deepai/deepai-catalog.ts +149 -0
  248. package/src/providers/integrations/deepai/deepai-common.ts +145 -0
  249. package/src/providers/integrations/deepai/deepai-oauth.ts +129 -0
  250. package/src/providers/integrations/deepai/deepai-quota.ts +97 -0
  251. package/src/providers/integrations/deepai/provider.ts +42 -0
  252. package/src/providers/integrations/devin/devin-auth.ts +7 -0
  253. package/src/providers/integrations/devin/protocol/devin-proto.ts +1 -27
  254. package/src/providers/integrations/factory/factory-oauth.ts +33 -51
  255. package/src/providers/integrations/freebuff/assets/official-system-manager.txt +88 -0
  256. package/src/providers/integrations/freebuff/assets/official-system-worker.txt +57 -0
  257. package/src/providers/integrations/freebuff/assets/official-tools.json +1070 -0
  258. package/src/providers/integrations/freebuff/freebuff-catalog.ts +120 -0
  259. package/src/providers/integrations/freebuff/freebuff-constants.ts +1233 -0
  260. package/src/providers/integrations/freebuff/freebuff-discovery.ts +108 -0
  261. package/src/providers/integrations/freebuff/freebuff-oauth.ts +240 -0
  262. package/src/providers/integrations/freebuff/freebuff-quota.ts +260 -0
  263. package/src/providers/integrations/freebuff/freebuff-virtual.ts +89 -0
  264. package/src/providers/integrations/freebuff/freebuff.ts +682 -0
  265. package/src/providers/integrations/freepi/freepi-catalog.ts +5 -0
  266. package/src/providers/integrations/freepi/freepi-discovery.ts +2 -2
  267. package/src/providers/integrations/freepi/freepi-headers.ts +2 -2
  268. package/src/providers/integrations/freepi/freepi-protocol.ts +11 -2
  269. package/src/providers/integrations/freepi/freepi-realm.ts +0 -3
  270. package/src/providers/integrations/freepi/freepi-session.ts +158 -0
  271. package/src/providers/integrations/freepi/freepi.ts +80 -54
  272. package/src/providers/integrations/github/github-discovery.ts +30 -13
  273. package/src/providers/integrations/github/github-oauth.ts +217 -44
  274. package/src/providers/integrations/github/github-quota.ts +22 -5
  275. package/src/providers/integrations/github/github.ts +40 -32
  276. package/src/providers/integrations/grok/grok-oauth.ts +52 -49
  277. package/src/providers/integrations/grok/grok-ratelimit-store.ts +10 -17
  278. package/src/providers/integrations/grok/grok-turn-index.ts +11 -16
  279. package/src/providers/integrations/hermes/hermes-oauth.ts +3 -3
  280. package/src/providers/integrations/{hermes.ts → hermes/hermes.ts} +42 -13
  281. package/src/providers/integrations/hermes/index.ts +2 -0
  282. package/src/providers/integrations/kilo/kilo-oauth.ts +4 -4
  283. package/src/providers/integrations/kilo/kilo.ts +1 -1
  284. package/src/providers/integrations/kimi/kimi-oauth.ts +3 -3
  285. package/src/providers/integrations/kiro/kiro-catalog.ts +17 -1
  286. package/src/providers/integrations/kiro/kiro-discovery.ts +10 -12
  287. package/src/providers/integrations/kiro/kiro-oauth.ts +2 -2
  288. package/src/providers/integrations/kiro/kiro-quota.ts +8 -4
  289. package/src/providers/integrations/kiro/kiro.ts +15 -19
  290. package/src/providers/integrations/muse/muse-oauth.ts +5 -5
  291. package/src/providers/integrations/ollamacloud/index.ts +2 -0
  292. package/src/providers/integrations/{ollamacloud-models.ts → ollamacloud/models.ts} +2 -2
  293. package/src/providers/integrations/{ollamacloud-quota.ts → ollamacloud/quota.ts} +3 -3
  294. package/src/providers/integrations/opencode/fingerprint.ts +1 -1
  295. package/src/providers/integrations/opencode/opencode-go-quota.ts +2 -8
  296. package/src/providers/integrations/openrouter/provider.ts +1 -1
  297. package/src/providers/integrations/perch/perch-credential.ts +25 -0
  298. package/src/providers/integrations/perch/perch-protocol.ts +0 -6
  299. package/src/providers/integrations/perch/perch-quota.ts +2 -16
  300. package/src/providers/integrations/perch/perch.ts +6 -16
  301. package/src/providers/integrations/qoder/claim-qoder-credits.ts +19 -60
  302. package/src/providers/integrations/qoder/qoder-adapter.ts +20 -125
  303. package/src/providers/integrations/qoder/qoder-auth.ts +109 -30
  304. package/src/providers/integrations/qoder/qoder-catalog.ts +21 -6
  305. package/src/providers/integrations/qoder/qoder-chat.ts +32 -29
  306. package/src/providers/integrations/qoder/qoder-checkin.ts +5 -5
  307. package/src/providers/integrations/qoder/qoder-cosy.ts +37 -9
  308. package/src/providers/integrations/qoder/qoder-oauth.ts +9 -2
  309. package/src/providers/integrations/qoder/qoder-realm.ts +13 -36
  310. package/src/providers/integrations/qoder/qoder-sse.ts +447 -68
  311. package/src/providers/integrations/qoder/qoder-types.ts +9 -0
  312. package/src/providers/integrations/qoder/qoder.ts +3 -26
  313. package/src/providers/integrations/sensenova/provider.ts +144 -0
  314. package/src/providers/integrations/trae/trae-protocol.ts +9 -5
  315. package/src/providers/integrations/trae/trae-quota.ts +21 -29
  316. package/src/providers/integrations/trae/trae-realm.ts +38 -0
  317. package/src/providers/integrations/trae/trae.ts +7 -24
  318. package/src/providers/integrations/vercel/provider.ts +117 -0
  319. package/src/providers/integrations/vercel/vercel-quota.ts +96 -0
  320. package/src/providers/integrations/xai/xai-oauth.ts +5 -5
  321. package/src/providers/integrations/zcode-start-plan/gifts.ts +1 -1
  322. package/src/providers/integrations/zcode-start-plan/oauth.ts +3 -3
  323. package/src/providers/integrations/zcode-start-plan/provider.ts +226 -23
  324. package/src/providers/integrations/zcode-start-plan/quota.ts +19 -2
  325. package/src/providers/integrations/zro/zro.ts +205 -0
  326. package/src/providers/operations/account-error-classification.ts +199 -307
  327. package/src/providers/operations/account-health-service.ts +82 -1
  328. package/src/providers/operations/account-refresh-status.ts +36 -37
  329. package/src/providers/operations/account-tier.ts +15 -16
  330. package/src/providers/operations/client-version-resolver.ts +1 -1
  331. package/src/providers/operations/client-versions.ts +100 -81
  332. package/src/providers/operations/cooldown-rules.ts +11 -16
  333. package/src/providers/operations/oauth-refresh-lead.ts +46 -22
  334. package/src/providers/operations/{model-catalog-cache.ts → provider-caches.ts} +68 -16
  335. package/src/providers/operations/provider-catalog-service.ts +1 -1
  336. package/src/providers/operations/provider-credential-service.ts +118 -51
  337. package/src/providers/operations/provider-rules/github.ts +48 -0
  338. package/src/providers/operations/provider-rules/grok.ts +33 -0
  339. package/src/providers/operations/provider-rules/inception.ts +49 -0
  340. package/src/providers/operations/provider-rules/index.ts +32 -0
  341. package/src/providers/operations/provider-rules/perch.ts +37 -0
  342. package/src/providers/operations/provider-rules/qoder.ts +70 -0
  343. package/src/providers/operations/provider-rules/trae.ts +81 -0
  344. package/src/providers/operations/session-resolution.ts +17 -1
  345. package/src/providers/provider-contracts.ts +6 -4
  346. package/src/providers/{provider-credential-resolver.ts → provider-credentials.ts} +40 -3
  347. package/src/providers/provider-metadata.ts +70 -10
  348. package/src/providers/provider-probe.ts +15 -1
  349. package/src/providers/provider-registry.ts +42 -33
  350. package/src/providers/quota/quota-contracts.ts +2 -0
  351. package/src/providers/quota/quota-limit-windows.ts +16 -5
  352. package/src/providers/quota/quota-support.ts +1 -1
  353. package/src/providers/quota/quota-window-parser.ts +90 -18
  354. package/src/providers/quota/refresh.ts +100 -21
  355. package/src/security/api-key-auth-cache.ts +1 -1
  356. package/src/security/api-key-auth.ts +6 -2
  357. package/src/security/ip-boundary.ts +1 -1
  358. package/src/transport/dispatch/attempt-finalize.ts +15 -14
  359. package/src/transport/dispatch/attempt-loop.ts +73 -19
  360. package/src/transport/dispatch/generation.ts +2 -8
  361. package/src/transport/dispatch/handler-deps.ts +1 -1
  362. package/src/transport/dispatch/leases.ts +1 -1
  363. package/src/transport/dispatch/native-shared.ts +25 -0
  364. package/src/transport/dispatch/proxy-request.ts +8 -46
  365. package/src/transport/dispatch/responses-compact.ts +2 -8
  366. package/src/transport/dispatch/retry-policy.ts +1 -1
  367. package/src/transport/dispatch/streaming/finalize.ts +10 -7
  368. package/src/transport/dispatch/streaming/stream.ts +9 -37
  369. package/src/transport/dispatch/systemone.ts +2 -8
  370. package/src/transport/dispatch/token-count.ts +2 -6
  371. package/src/transport/dispatch/upstream.ts +63 -0
  372. package/src/transport/drain-endpoint.ts +31 -4
  373. package/src/transport/failure-policy.ts +16 -30
  374. package/src/transport/guards/body-policy.ts +7 -1
  375. package/src/transport/guards/error-lifecycle.ts +13 -1
  376. package/src/transport/guards/request-context.ts +22 -14
  377. package/src/transport/request/preparer.ts +81 -71
  378. package/src/transport/routing/bounded-lru.ts +14 -0
  379. package/src/transport/routing/drain-reserve.ts +129 -0
  380. package/src/transport/routing/plan-gate.ts +7 -1
  381. package/src/transport/routing/round-robin.ts +60 -0
  382. package/src/transport/routing/route-catalog.ts +123 -111
  383. package/src/transport/routing/route-model.ts +51 -9
  384. package/src/transport/routing/router.ts +393 -32
  385. package/src/transport/shutdown-notice.ts +1 -1
  386. package/src/transport/surface/adapters.ts +11 -2
  387. package/src/transport/surface/chat/adapter.ts +2 -2
  388. package/src/transport/surface/chat/encode.ts +47 -4
  389. package/src/transport/surface/chat/parse.ts +2 -12
  390. package/src/transport/surface/completion.ts +44 -33
  391. package/src/transport/surface/content-parts.ts +21 -0
  392. package/src/transport/surface/messages/adapter.ts +7 -1
  393. package/src/transport/surface/messages/encode.ts +11 -3
  394. package/src/transport/surface/messages/parse.ts +2 -1
  395. package/src/transport/surface/messages/stream.ts +33 -17
  396. package/src/transport/surface/responses/encode.ts +22 -5
  397. package/src/transport/surface/responses/parse.ts +3 -11
  398. package/src/workers/buddy-growth-tasks.ts +8 -8
  399. package/src/workers/daily-checkin.ts +4 -4
  400. package/src/workers/quota-refresh-sweep.ts +1 -1
  401. package/src/workers/zcode-gift-claim.ts +1 -1
  402. package/dist/dashboard/assets/BannedPage-BxoG1MrL.js.gz +0 -0
  403. package/dist/dashboard/assets/CliToolDetailPage-ixwgKk53.js.gz +0 -0
  404. package/dist/dashboard/assets/CliToolsPage-BmttNV3U.js.gz +0 -0
  405. package/dist/dashboard/assets/CombosPage-BE3qwwfC.js.gz +0 -0
  406. package/dist/dashboard/assets/ConsoleLogPage-CRZdmMIX.js.gz +0 -0
  407. package/dist/dashboard/assets/CustomizationPage-Dv_FJ6Er.js.gz +0 -0
  408. package/dist/dashboard/assets/HealthEventsModal-CoVrw2sM.js +0 -1
  409. package/dist/dashboard/assets/HealthEventsModal-CoVrw2sM.js.gz +0 -0
  410. package/dist/dashboard/assets/InjectSystemPromptPanel-BOoO8rpz.js.gz +0 -0
  411. package/dist/dashboard/assets/LoginPage-ChJuNNVN.js.gz +0 -0
  412. package/dist/dashboard/assets/ModelPicker-BZAJtPg1.js.gz +0 -0
  413. package/dist/dashboard/assets/OverviewPage-BE40s3u5.js.gz +0 -0
  414. package/dist/dashboard/assets/ProviderDetailPage-sfLoQtPV.js +0 -8
  415. package/dist/dashboard/assets/ProviderDetailPage-sfLoQtPV.js.gz +0 -0
  416. package/dist/dashboard/assets/ProviderIcon-CQ6bPUf_.js.gz +0 -0
  417. package/dist/dashboard/assets/ProvidersPage-DcepmPWo.js +0 -1
  418. package/dist/dashboard/assets/ProvidersPage-DcepmPWo.js.gz +0 -0
  419. package/dist/dashboard/assets/ProxyPage-BJ87Pp0U.js +0 -9
  420. package/dist/dashboard/assets/ProxyPage-BJ87Pp0U.js.gz +0 -0
  421. package/dist/dashboard/assets/QuotaPage-gkSyRAKa.js +0 -1
  422. package/dist/dashboard/assets/QuotaPage-gkSyRAKa.js.gz +0 -0
  423. package/dist/dashboard/assets/SettingsPage-C5HPWqEv.js.gz +0 -0
  424. package/dist/dashboard/assets/SetupPage-CFNcATJJ.js.gz +0 -0
  425. package/dist/dashboard/assets/StudioPage-YoD4tY5l.js.gz +0 -0
  426. package/dist/dashboard/assets/UsagePage-DxjaAhb_.js.gz +0 -0
  427. package/dist/dashboard/assets/api-keys-BTlkq_Uk.js.gz +0 -0
  428. package/dist/dashboard/assets/app-BX2nnKqs.css +0 -2
  429. package/dist/dashboard/assets/app-BX2nnKqs.css.gz +0 -0
  430. package/dist/dashboard/assets/assert-CndthXFQ.js +0 -1
  431. package/dist/dashboard/assets/assert-CndthXFQ.js.gz +0 -0
  432. package/dist/dashboard/assets/badge-DdbT_7Am.js.gz +0 -0
  433. package/dist/dashboard/assets/banner-CsGN-nbm.js.gz +0 -0
  434. package/dist/dashboard/assets/card-mgZ_TLLZ.js.gz +0 -0
  435. package/dist/dashboard/assets/cli-tools-A1-v8-03.js.gz +0 -0
  436. package/dist/dashboard/assets/clipboard-button-CJ4Zty89.js.gz +0 -0
  437. package/dist/dashboard/assets/dialog-B5L2jPQ_.js.gz +0 -0
  438. package/dist/dashboard/assets/drawer-DkjUouAj.js.gz +0 -0
  439. package/dist/dashboard/assets/entry-B-LVLV6o.js.gz +0 -0
  440. package/dist/dashboard/assets/entry-CAhdYl3B.js.gz +0 -0
  441. package/dist/dashboard/assets/entry-D2xD0Tmd.js +0 -4
  442. package/dist/dashboard/assets/entry-D2xD0Tmd.js.gz +0 -0
  443. package/dist/dashboard/assets/formatters-CrsdLVsn.js.gz +0 -0
  444. package/dist/dashboard/assets/index-BU64Lqvp.js.gz +0 -0
  445. package/dist/dashboard/assets/inline-B2aDLyyg.js.gz +0 -0
  446. package/dist/dashboard/assets/input-BCVkdR9d.js.gz +0 -0
  447. package/dist/dashboard/assets/layout-DNcAdMK-.js.gz +0 -0
  448. package/dist/dashboard/assets/list-row-DgrIH0hw.js.gz +0 -0
  449. package/dist/dashboard/assets/live-Cae01iu8.js.gz +0 -0
  450. package/dist/dashboard/assets/network-CRib7H_2.js.gz +0 -0
  451. package/dist/dashboard/assets/numbers-BKQE8Xpz.js.gz +0 -0
  452. package/dist/dashboard/assets/pagination-ecPvG_8g.js.gz +0 -0
  453. package/dist/dashboard/assets/quota-DJ1QlAOq.js +0 -1
  454. package/dist/dashboard/assets/quota-DJ1QlAOq.js.gz +0 -0
  455. package/dist/dashboard/assets/routing-Ckm1UXuC.js.gz +0 -0
  456. package/dist/dashboard/assets/select-ChoFWyKz.js.gz +0 -0
  457. package/dist/dashboard/assets/settings-CKjFPlKW.js.gz +0 -0
  458. package/dist/dashboard/assets/stack-DWCwU3fh.js.gz +0 -0
  459. package/dist/dashboard/assets/state-Cg3VL5J_.js.gz +0 -0
  460. package/dist/dashboard/assets/switch-Db70BMix.js.gz +0 -0
  461. package/dist/dashboard/assets/useMutation-BqdE0LuM.js.gz +0 -0
  462. package/src/network/pool-health.ts +0 -210
  463. package/src/providers/bundled-discovery-providers.ts +0 -98
  464. package/src/providers/configured-openai-providers.ts +0 -120
  465. package/src/providers/integrations/buddy/buddy-checkin.ts +0 -521
  466. package/src/providers/integrations/buddy/buddy-cli-headers.ts +0 -76
  467. package/src/providers/integrations/buddy/buddy-conversation-headers.ts +0 -130
  468. package/src/providers/integrations/buddy/buddy-discovery-shared.ts +0 -234
  469. package/src/providers/integrations/buddy/buddy-growth-shared.ts +0 -496
  470. package/src/providers/integrations/buddy/buddy-identity.ts +0 -300
  471. package/src/providers/integrations/buddy/buddy-oauth-shared.ts +0 -341
  472. package/src/providers/integrations/buddy/buddy-quota-shared.ts +0 -417
  473. package/src/providers/integrations/buddy/buddy-server-tools.ts +0 -240
  474. package/src/providers/integrations/buddy/buddy-usage-shared.ts +0 -221
  475. package/src/providers/integrations/buddy/buddy-usage-store.ts +0 -181
  476. package/src/providers/integrations/buddy/codebuddy/cn-catalog.ts +0 -37
  477. package/src/providers/integrations/buddy/codebuddy/cn-provider.ts +0 -185
  478. package/src/providers/integrations/buddy/codebuddy/oauth.ts +0 -47
  479. package/src/providers/integrations/buddy/codebuddy/provider.ts +0 -175
  480. package/src/providers/integrations/buddy/codebuddy/quota.ts +0 -154
  481. package/src/providers/integrations/qoder/chat-proto.ts +0 -328
  482. package/src/providers/integrations/qoder/qoder-grpc.ts +0 -871
  483. package/src/providers/integrations/qoder/qoder-ide.ts +0 -588
  484. package/src/providers/operations/model-discovery-cache.ts +0 -34
  485. package/src/providers/operations/probe-fetch.ts +0 -15
  486. package/src/providers/operations/provider-version-cache.ts +0 -30
  487. package/src/providers/provider-capabilities.ts +0 -33
  488. package/src/providers/provider-credential-envelope.ts +0 -43
  489. /package/dist/dashboard/assets/{app-B7kIWFm5.js → app-C0gTXEoN.js} +0 -0
  490. /package/dist/dashboard/assets/{app-B7kIWFm5.js.gz → app-C0gTXEoN.js.gz} +0 -0
  491. /package/src/network/{http2-bidi.ts → bidi.ts} +0 -0
  492. /package/src/network/pool/{pool-scripts.ts → scripts.ts} +0 -0
  493. /package/src/network/pool/{pool-state.ts → state.ts} +0 -0
  494. /package/src/network/{backoff.ts → util/backoff.ts} +0 -0
  495. /package/src/network/{deduplication.ts → util/dedupe.ts} +0 -0
  496. /package/src/network/{ip-utils.ts → util/ip.ts} +0 -0
@@ -29,7 +29,7 @@ import {
29
29
  type QuotaRefreshFailureCode,
30
30
  } from "../../providers/quota/refresh";
31
31
  import { selectAccountTestEgress } from "./account-test-egress";
32
- import type { ValidatedFetch } from "../../network/outbound-fetch";
32
+ import type { ValidatedFetch } from "../../network/egress/fetch";
33
33
  import type { ProviderId } from "../../providers/provider-contracts";
34
34
  import {
35
35
  DAILY_CHECKIN_PROVIDER_IDS,
@@ -43,7 +43,7 @@ import {
43
43
  fetchDailyCheckin,
44
44
  type BuddyActivityReportResult,
45
45
  type DailyCheckinResult,
46
- } from "../../providers/integrations/buddy/buddy-checkin";
46
+ } from "../../providers/integrations/buddy/lifecycle";
47
47
  import {
48
48
  fetchQoderDailyCheckin,
49
49
  isQoderCheckinProvider,
@@ -72,9 +72,8 @@ import {
72
72
  toQuotaView,
73
73
  } from "./quota-view";
74
74
  import {
75
- ACCOUNT_STATUSES,
75
+ resolveNextAccountStatus,
76
76
  type AccountQuotaRoutesDeps,
77
- type AccountStatus,
78
77
  } from "./account-quota-shared";
79
78
  import {
80
79
  DAILY_GROWTH_LEG_TIMEOUT_MS,
@@ -195,7 +194,7 @@ async function runDailyGrowthForAccount(
195
194
  // The buddy-oauth-shared graph is lazy-loaded: the uid decoder is only
196
195
  // needed for buddy accounts' report leg (B-02).
197
196
  const { buddyAccountUid } = await import(
198
- "../../providers/integrations/buddy/buddy-oauth-shared"
197
+ "../../providers/integrations/buddy/account"
199
198
  );
200
199
  const userId = buddyAccountUid(credential);
201
200
  let report: { ok: boolean; message: string };
@@ -428,7 +427,7 @@ export function registerAccountQuotaTenantRoutes(
428
427
  // The buddy-usage-store graph is lazy-loaded: it is only needed when the
429
428
  // page actually renders buddy accounts (B-02).
430
429
  const { readBuddyUsageSnapshots } = await import(
431
- "../../providers/integrations/buddy/buddy-usage-store"
430
+ "../../providers/integrations/buddy/account"
432
431
  );
433
432
  const usageByAccount = await readBuddyUsageSnapshots(
434
433
  db,
@@ -745,23 +744,12 @@ export function registerAccountQuotaTenantRoutes(
745
744
  },
746
745
  async ({ request, params, body, set }) => {
747
746
  const access = requireTenantScope(accessResolver(request), "dashboard:write");
748
- if (body.status !== undefined && !ACCOUNT_STATUSES.includes(body.status as AccountStatus)) {
749
- set.status = 400;
750
- return {
751
- error: `status must be one of: ${ACCOUNT_STATUSES.join(", ")}`,
752
- code: "invalid_status",
753
- };
754
- }
755
- const nextStatus: AccountStatus | undefined =
756
- body.active !== undefined
757
- ? body.active
758
- ? "active"
759
- : "disabled"
760
- : (body.status as AccountStatus | undefined);
761
- if (!nextStatus) {
762
- set.status = 400;
763
- return { error: "active or status is required", code: "invalid_request" };
747
+ const resolved = resolveNextAccountStatus(body);
748
+ if (!resolved.ok) {
749
+ set.status = resolved.status;
750
+ return resolved.body;
764
751
  }
752
+ const nextStatus = resolved.nextStatus;
765
753
  const updated = await db
766
754
  .update(providerAccounts)
767
755
  .set({ status: nextStatus })
@@ -1262,7 +1250,7 @@ export function registerAccountQuotaTenantRoutes(
1262
1250
  // The buddy-oauth-shared graph is lazy-loaded: the uid decoder is only
1263
1251
  // needed on this buddy-only surface (B-02).
1264
1252
  const { buddyAccountUid } = await import(
1265
- "../../providers/integrations/buddy/buddy-oauth-shared"
1253
+ "../../providers/integrations/buddy/account"
1266
1254
  );
1267
1255
  const userId = buddyAccountUid(credential);
1268
1256
  if (!userId) {
@@ -11,8 +11,8 @@ import { networkPools, poolRoutingSettings, providerRoutingSettings } from "../.
11
11
  import { resolveTenantOverride } from "../../persistence/tenant-scope";
12
12
  import { GatewayError } from "../../protocol/gateway-error";
13
13
  import type { NetworkPoolSelector, PoolRotation } from "../../network/pool/selector";
14
- import type { ValidatedFetch } from "../../network/outbound-fetch";
15
- import type { ValidatedNetworkBindingFactory } from "../../network/pool/resolver";
14
+ import type { ValidatedFetch } from "../../network/egress/fetch";
15
+ import type { ValidatedNetworkBindingFactory } from "../../network/egress/factory";
16
16
  import { DEFAULT_PROXY_SKIP_PROVIDER_IDS } from "../../providers/provider-metadata";
17
17
  import type { FetchLike } from "../../providers/quota/quota-contracts";
18
18
 
@@ -31,7 +31,7 @@ import { randomUUID } from "node:crypto";
31
31
  import type { CartethyiaDatabase } from "../../persistence/db-handle";
32
32
  import type { RedisClient } from "../../persistence/redis";
33
33
  import { checkinAttemptedToday } from "../../workers/daily-checkin";
34
- import type { ValidatedFetch } from "../../network/outbound-fetch";
34
+ import type { ValidatedFetch } from "../../network/egress/fetch";
35
35
 
36
36
  /** Lifecycle states of a daily-growth job. */
37
37
  export type DailyGrowthJobState = "queued" | "running" | "done" | "failed";
@@ -13,7 +13,14 @@ import {
13
13
  /** Shape returned for quota data on dashboard account responses. */
14
14
  interface QuotaRefreshView {
15
15
  readonly source: string;
16
- readonly status: "ready" | "error" | "refreshing";
16
+ /**
17
+ * `ready` = a usable quota reading exists (successful fetch, cached or
18
+ * snapshot). `error` = the collector returned an error with no usable
19
+ * reading. `refreshing` = a background fill is in flight. `unknown` = no
20
+ * quota evidence at all (no collector surface, or an empty successful read
21
+ * with no timestamp) — the canonical wire representation for unknown quota.
22
+ */
23
+ readonly status: "ready" | "error" | "refreshing" | "unknown";
17
24
  readonly plan: string | null;
18
25
  readonly windows: ProviderQuotaResult["windows"];
19
26
  readonly fetchedAt: string | null;
@@ -94,7 +101,7 @@ export function toQuotaView(
94
101
  lastAttemptAt?: string | null;
95
102
  lastSuccessAt?: string | null;
96
103
  refreshError?: string | null;
97
- status?: "ready" | "error" | "refreshing";
104
+ status?: "ready" | "error" | "refreshing" | "unknown";
98
105
  } = {},
99
106
  ): QuotaRefreshView {
100
107
  const fetchedAt = timestamps.fetchedAt ?? account.lastSuccessAt?.toISOString() ?? null;
@@ -112,9 +119,18 @@ export function toQuotaView(
112
119
  // that the last attempt failed; only `quota.error` — no usable reading at
113
120
  // all — makes the card an error.
114
121
  const error = quota.error ?? null;
122
+ // Unknown quota: a successful read that carries no windows AND no timestamp
123
+ // is not evidence of anything — the canonical "no quota evidence" state.
124
+ // (A provider with a real zero-balance still reports windows/timestamps.)
125
+ const derivedStatus =
126
+ error !== null
127
+ ? ("error" as const)
128
+ : quota.windows.length === 0 && fetchedAt === null
129
+ ? ("unknown" as const)
130
+ : ("ready" as const);
115
131
  return {
116
132
  source: quota.source,
117
- status: timestamps.status ?? (error !== null ? "error" : "ready"),
133
+ status: timestamps.status ?? derivedStatus,
118
134
  plan: quota.plan ?? null,
119
135
  windows: quota.windows,
120
136
  fetchedAt,
@@ -10,7 +10,7 @@ import { isAddressAllowed } from "../../../network/ssrf";
10
10
  import { TRANSPORT_KINDS, type TransportKind } from "../../../network/pool/agent";
11
11
  import type { SsrfPolicy } from "../../../config/network";
12
12
  import type { NetworkPoolSelector } from "../../../network/pool/selector";
13
- import type { PoolHealthEvent } from "../../../network/pool-health-machine";
13
+ import type { PoolHealthEvent } from "../../../network/pool/health";
14
14
 
15
15
  export interface CreateNetworkPoolRequest {
16
16
  kind: TransportKind;
@@ -4,7 +4,7 @@
4
4
  import { ConsoleDomainError, requireTenantScope } from "../../shared/errors";
5
5
  import { providerRateLimitIsIpScoped } from "../../../providers/provider-metadata";
6
6
  import { resolvePoolProviderRateLimitCooldownMs } from "../../../config/cooldowns";
7
- import type { PoolHealthEvent } from "../../../network/pool-health-machine";
7
+ import type { PoolHealthEvent } from "../../../network/pool/health";
8
8
  import type { AccessDecision } from "../../../security/access-control";
9
9
  import {
10
10
  deployRelay as deployRelayWorker,
@@ -45,35 +45,40 @@ function isBlockedEntry(entry: { readonly blocked?: boolean }): boolean {
45
45
  }
46
46
 
47
47
  export function createNetworkPoolOperations(config: NetworkPoolConfig) {
48
+ /**
49
+ * The read-only enrichment projection shared by the pool list and the pool
50
+ * detail page: live inflight (falling back to the stored row counter when no
51
+ * selector is configured), the derived available/utilization figures, and the
52
+ * active provider cooldowns (omitted entirely when there are none).
53
+ */
54
+ async function enrichPoolResponse(res: NetworkPoolResponse, poolId: string): Promise<NetworkPoolResponse> {
55
+ const cooldowns = await config.poolSelector?.getPoolCooldowns(poolId);
56
+ const inflight = config.poolSelector
57
+ ? await config.poolSelector.getInflightAuthoritative(poolId)
58
+ : res.inflight;
59
+ return {
60
+ ...res,
61
+ inflight,
62
+ available: Math.max(0, res.maxInflight - inflight),
63
+ utilization: res.maxInflight > 0 ? Math.min(1, inflight / res.maxInflight) : 1,
64
+ ...(cooldowns && cooldowns.length > 0
65
+ ? {
66
+ providerCooldowns: cooldowns.map((c) => ({
67
+ providerId: c.providerId,
68
+ until: new Date(c.until).toISOString(),
69
+ reason: c.reason,
70
+ blocked: isBlockedEntry(c),
71
+ })),
72
+ }
73
+ : {}),
74
+ };
75
+ }
76
+
48
77
  const operations = {
49
78
  async listPools(access: AccessDecision | undefined): Promise<NetworkPoolResponse[]> {
50
79
  const a = requireTenantScope(access, "dashboard:read");
51
80
  const rawPools = await config.store.list(a.tenantId);
52
- return Promise.all(
53
- rawPools.map(async (r) => {
54
- const res = sanitizePoolResponse(r);
55
- const cooldowns = await config.poolSelector?.getPoolCooldowns(r.id);
56
- const inflight = config.poolSelector
57
- ? await config.poolSelector.getInflightAuthoritative(r.id)
58
- : res.inflight;
59
- return {
60
- ...res,
61
- inflight,
62
- available: Math.max(0, res.maxInflight - inflight),
63
- utilization: res.maxInflight > 0 ? Math.min(1, inflight / res.maxInflight) : 1,
64
- ...(cooldowns && cooldowns.length > 0
65
- ? {
66
- providerCooldowns: cooldowns.map((c) => ({
67
- providerId: c.providerId,
68
- until: new Date(c.until).toISOString(),
69
- reason: c.reason,
70
- blocked: isBlockedEntry(c),
71
- })),
72
- }
73
- : {}),
74
- };
75
- }),
76
- );
81
+ return Promise.all(rawPools.map(async (r) => enrichPoolResponse(sanitizePoolResponse(r), r.id)));
77
82
  },
78
83
  async getPoolDetail(
79
84
  access: AccessDecision | undefined,
@@ -82,27 +87,7 @@ export function createNetworkPoolOperations(config: NetworkPoolConfig) {
82
87
  const a = requireTenantScope(access, "dashboard:read");
83
88
  const rec = await config.store.get(a.tenantId, poolId);
84
89
  if (!rec) throw new ConsoleDomainError("pool_not_found", 404, `Pool ${poolId} not found`);
85
- const res = sanitizePoolResponse(rec);
86
- const cooldowns = await config.poolSelector?.getPoolCooldowns(poolId);
87
- const inflight = config.poolSelector
88
- ? await config.poolSelector.getInflightAuthoritative(poolId)
89
- : res.inflight;
90
- return {
91
- ...res,
92
- inflight,
93
- available: Math.max(0, res.maxInflight - inflight),
94
- utilization: res.maxInflight > 0 ? Math.min(1, inflight / res.maxInflight) : 1,
95
- ...(cooldowns && cooldowns.length > 0
96
- ? {
97
- providerCooldowns: cooldowns.map((c) => ({
98
- providerId: c.providerId,
99
- until: new Date(c.until).toISOString(),
100
- reason: c.reason,
101
- blocked: isBlockedEntry(c),
102
- })),
103
- }
104
- : {}),
105
- };
90
+ return enrichPoolResponse(sanitizePoolResponse(rec), poolId);
106
91
  },
107
92
  async clearPoolCooldown(
108
93
  access: AccessDecision | undefined,
@@ -17,13 +17,13 @@
17
17
  * pool for good.
18
18
  *
19
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
20
+ * `classifyEgressFailure` (in `network/verdict`) reads on the
21
21
  * dispatch path — for the probe they arrive as an HTTP status + body instead of
22
22
  * a `GatewayError`, so the shared free-tier regex is borrowed from there to
23
23
  * keep the two classifiers' boundaries honest.
24
24
  */
25
25
 
26
- import { FREE_TIER_REFUSAL } from "../../../network/rate-limit-verdict";
26
+ import { FREE_TIER_REFUSAL } from "../../../network/verdict";
27
27
 
28
28
  export type ProbeVerdict = "allowed" | "blocked" | "throttled" | "unreachable";
29
29
 
@@ -5,7 +5,7 @@
5
5
  * Cartethyia already knows how to *use* a hosted relay: a pool whose endpoint
6
6
  * host is `*.workers.dev` / `*.vercel.app` / `*.deno.dev` is classified as an
7
7
  * application relay and dialed with `x-relay-target` / `x-relay-path` headers
8
- * (see `network/pool/agent.ts` and `network/outbound-fetch.ts`). This module
8
+ * (see `network/pool/agent.ts` and `network/egress/fetch.ts`). This module
9
9
  * supplies the other half — the operator does not have to write and deploy the
10
10
  * worker by hand.
11
11
  *
@@ -16,7 +16,7 @@ import {
16
16
  } from "./contracts";
17
17
  import { classifyPoolConnectError, classifyPoolProbeResponse, parseEgressIp } from "./probe-result";
18
18
  import { encryptCredential } from "../../../security/crypto";
19
- import { createValidatedFetch } from "../../../network/outbound-fetch";
19
+ import { createValidatedFetch } from "../../../network/egress/fetch";
20
20
  import { classifyProviderProbeAnswer } from "./probe-verdict";
21
21
  import { buildProviderProbeRequest } from "../../../providers/provider-probe";
22
22
  import type { StoredProviderProbe } from "./contracts";
@@ -34,9 +34,9 @@ import {
34
34
  disablePoolForProxyHttpStatus,
35
35
  listNetworkPoolHealthEvents,
36
36
  recoverNetworkPool,
37
- } from "../../../network/pool-health-machine";
38
- import { drainPoolByteDelta, recordPoolBytes } from "../../../network/pool/byte-accounting";
39
- import * as poolCooldownStore from "../../../network/pool-cooldown-store";
37
+ } from "../../../network/pool/health";
38
+ import { drainPoolByteDelta, recordPoolBytes } from "../../../network/pool/accounting";
39
+ import * as poolCooldownStore from "../../../network/pool/cooldown-store";
40
40
  // Cloudflare's trace endpoint answers with plain `key=value` lines including
41
41
  // `ip=`, the address the request egressed from. Dialing it THROUGH the pool
42
42
  // therefore reports the pool's public address — which is what an operator
@@ -1,7 +1,7 @@
1
1
  import { ValidationError } from "elysia";
2
2
  import type { AccessDecision, AccessScope } from "../../security/access-control";
3
3
  import { isRecord } from "../../protocol/codecs";
4
- import { formatPublicErrorMessage } from "../../protocol/gateway-error";
4
+ import { formatPublicErrorMessage, GatewayError } from "../../protocol/gateway-error";
5
5
 
6
6
  // Single console error/access contract. Every domain throws ConsoleDomainError
7
7
  // (or the storage-layer BackupError, same code/status/message shape) and
@@ -24,8 +24,27 @@ export class ConsoleDomainError extends Error {
24
24
  }
25
25
  }
26
26
 
27
- function asDomainError(error: unknown): { code: string; status: number; message: string; details?: unknown } | undefined {
27
+ interface ShapedDomainError {
28
+ readonly code: string;
29
+ readonly status: number;
30
+ readonly message: string;
31
+ readonly details?: unknown;
32
+ /** Upstream/network origin, preserved so the dashboard can tell a provider
33
+ * rejection from a console-session expiry (see `maybeSignalSessionTransition`). */
34
+ readonly origin?: string;
35
+ }
36
+
37
+ function asDomainError(error: unknown): ShapedDomainError | undefined {
28
38
  if (error instanceof ConsoleDomainError) return error;
39
+ if (error instanceof GatewayError) {
40
+ return {
41
+ code: error.code,
42
+ status: error.status,
43
+ message: error.message,
44
+ details: error.details,
45
+ ...(error.origin === "cartethyia" ? {} : { origin: error.origin }),
46
+ };
47
+ }
29
48
  if (
30
49
  typeof error === "object" &&
31
50
  error !== null &&
@@ -33,8 +52,14 @@ function asDomainError(error: unknown): { code: string; status: number; message:
33
52
  typeof (error as { status?: unknown }).status === "number" &&
34
53
  typeof (error as { message?: unknown }).message === "string"
35
54
  ) {
36
- const shaped = error as { code: string; status: number; message: string; details?: unknown };
37
- return { code: shaped.code, status: shaped.status, message: shaped.message, details: shaped.details };
55
+ const shaped = error as { code: string; status: number; message: string; details?: unknown; origin?: unknown };
56
+ return {
57
+ code: shaped.code,
58
+ status: shaped.status,
59
+ message: shaped.message,
60
+ details: shaped.details,
61
+ ...(typeof shaped.origin === "string" && shaped.origin !== "cartethyia" ? { origin: shaped.origin } : {}),
62
+ };
38
63
  }
39
64
  return undefined;
40
65
  }
@@ -47,6 +72,7 @@ export function errorResponse(
47
72
  ): {
48
73
  error: string;
49
74
  code: string;
75
+ origin?: string;
50
76
  details?: Record<string, unknown>;
51
77
  } {
52
78
  const shaped = asDomainError(error);
@@ -63,16 +89,17 @@ export function errorResponse(
63
89
  const body: Record<string, unknown> = {
64
90
  error: message,
65
91
  code: shaped.code,
92
+ ...(shaped.origin === undefined ? {} : { origin: shaped.origin }),
66
93
  };
67
94
  if (includeDetails) body.details = details;
68
- return body as { error: string; code: string; details?: Record<string, unknown> };
95
+ return body as { error: string; code: string; origin?: string; details?: Record<string, unknown> };
69
96
  }
70
97
  set.status = 500;
71
98
  const body: Record<string, unknown> = {
72
99
  error: formatPublicErrorMessage("internal_error", fallbackMessage),
73
100
  code: "internal_error",
74
101
  };
75
- return body as { error: string; code: string; details?: Record<string, unknown> };
102
+ return body as { error: string; code: string; origin?: string; details?: Record<string, unknown> };
76
103
  }
77
104
 
78
105
  export function requireScope(
package/src/main.ts CHANGED
@@ -3,6 +3,7 @@
3
3
  import "./boot/elysia-typebox-bridge";
4
4
  import { createGatewayApp, createGatewayShell } from "./boot/app";
5
5
  import { bootstrap } from "./boot/lifecycle";
6
+ import { flushAllBuffers } from "./boot/flush";
6
7
  import type { CartethyiaBoot } from "./boot/lifecycle";
7
8
  import {
8
9
  resolveDrainToken,
@@ -90,9 +91,7 @@ const app = boot
90
91
  // for the queues, minus the stop.
91
92
  flushBuffers: {
92
93
  flush: async () => {
93
- await boot.deps.telemetryBuffer.flush();
94
- await boot.deps.usageFlush.flush();
95
- await boot.deps.healthEventFlush.flush();
94
+ await flushAllBuffers(boot.deps);
96
95
  },
97
96
  },
98
97
  },
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Unified network egress client.
3
+ *
4
+ * One entry point for every outbound transport the gateway can dial:
5
+ *
6
+ * - `fetch(poolId?, tenantId?)` — validated HTTP fetch (SSRF-guarded), with
7
+ * optional TLS ClientHello profile support (`options.tlsProfile`).
8
+ * - `bidi(poolId?, tenantId?)` — validated bidirectional HTTP/2 stream
9
+ * (Connect-RPC-style request/response over one long-lived stream).
10
+ * - `webSocket(poolId?, tenantId?)` — validated WSS session.
11
+ * - `resolve(hostname, signal, port?)` — one-shot DNS resolution + SSRF
12
+ * validation, pinned so a rebind cannot retarget the socket.
13
+ *
14
+ * All four bind to the same selected network pool (when one is given) or to
15
+ * validated direct egress. Provider adapters call this and never dial a
16
+ * socket themselves.
17
+ */
18
+ import { ValidatedNetworkBindingFactory } from "./egress/factory";
19
+ import type { ValidatedOutboundHttp2Bidi } from "./bidi";
20
+ import type { ValidatedOutboundWebSocket } from "../providers/provider-contracts";
21
+ import type { ValidatedDestination } from "./ssrf";
22
+ import type { SsrfPolicy } from "../config/network";
23
+ import type { TlsProfile } from "./egress/tls-profile";
24
+ import type { ValidatedFetch } from "./egress/fetch";
25
+ import type { PoolAgentResolver } from "./pool/resolver";
26
+
27
+ export interface NetworkClientOptions {
28
+ /** SSRF policy applied to every dial. Defaults to the empty (block-all-loopback) policy. */
29
+ readonly policy?: SsrfPolicy;
30
+ /** Injected fetch for offline tests/recordings. */
31
+ readonly fetchFn?: typeof fetch;
32
+ /** Optional pool-agent resolver; without it every dial is direct. */
33
+ readonly poolResolver?: PoolAgentResolver;
34
+ /**
35
+ * Default TLS ClientHello profile for direct https dials. `"default"` keeps
36
+ * the runtime's own TLS; a named profile (see `tls-profile.ts`) dials with
37
+ * that profile's OpenSSL options so the wire fingerprint matches the
38
+ * impersonated client.
39
+ */
40
+ readonly tlsProfile?: TlsProfile;
41
+ }
42
+
43
+ export interface NetworkClient {
44
+ /** Validated HTTP fetch bound to a pool slot (or direct when omitted). */
45
+ readonly fetch: (poolId?: string, tenantId?: string) => ValidatedFetch;
46
+ /** Validated bidirectional HTTP/2 stream. */
47
+ readonly bidi: (poolId?: string, tenantId?: string) => ValidatedOutboundHttp2Bidi;
48
+ /** Validated WSS session. */
49
+ readonly webSocket: (poolId?: string, tenantId?: string) => ValidatedOutboundWebSocket;
50
+ /** One-shot DNS resolution + SSRF validation for direct egress targets. */
51
+ readonly resolve: (hostname: string, signal: AbortSignal, port?: number) => Promise<ValidatedDestination>;
52
+ }
53
+
54
+ export function createNetworkClient(options: NetworkClientOptions = {}): NetworkClient {
55
+ const factory = new ValidatedNetworkBindingFactory(
56
+ options.policy,
57
+ options.fetchFn,
58
+ options.poolResolver,
59
+ options.tlsProfile,
60
+ );
61
+ return {
62
+ fetch: (poolId, tenantId) => factory.fetch(poolId, tenantId),
63
+ bidi: (poolId, tenantId) => factory.http2Bidi(poolId, tenantId),
64
+ webSocket: (poolId, tenantId) => factory.webSocket(poolId, tenantId),
65
+ resolve: (hostname, signal, port) => factory.resolve(hostname, signal, port),
66
+ };
67
+ }