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
@@ -27,7 +27,7 @@ import { mapUpstreamHttpError } from "../../../transport/failure-policy";
27
27
  import type { ImageGenerationOutcome, ProviderDispatchContext, ProviderDispatchTarget, ValidatedOutboundFetch } from "../../provider-contracts";
28
28
  import type { CanonicalRequest } from "../../../protocol/canonical-model";
29
29
  import { codebuddyHeaders, type CodeBuddyVariant } from "./codebuddy/shared";
30
- import { applyBuddySystemPrompt, buddyPrePayloadCommon } from "./buddy-chat-shared";
30
+ import { applyBuddySystemPrompt, buddyPrePayloadCommon } from "./chat";
31
31
 
32
32
  /** Media adapter identity: the variant's headers, its leading system turn, and base URL. */
33
33
  export interface BuddyMediaConfig {
@@ -102,9 +102,13 @@ export async function fetchClaudeQuota(
102
102
  credential: string,
103
103
  fetcher: FetchLike,
104
104
  ): Promise<ProviderQuotaResult> {
105
+ // No module-local quota cache: the shared two-tier quota cache owns
106
+ // cross-call dedup, and a local tier keyed by credential only is never
107
+ // invalidated by `debitCachedQuota`, so a sweep within its TTL could
108
+ // resurrect a spent balance over the shared cache. Every call here is a
109
+ // fresh upstream read; the shared cache's in-flight dedup prevents
110
+ // duplicate traffic.
105
111
  const key = sha256Hex(credential);
106
- const cached = quotaCache.get(key);
107
- if (cached !== undefined) return cached;
108
112
  if (quotaCooldown.get(key) !== undefined) {
109
113
  throw new Error("Quota endpoint rejected request: rate limited (cooled down)");
110
114
  }
@@ -131,9 +135,7 @@ export async function fetchClaudeQuota(
131
135
  },
132
136
  observing,
133
137
  );
134
- const result = parseClaudeQuota(body);
135
- quotaCache.set(key, result);
136
- return result;
138
+ return parseClaudeQuota(body);
137
139
  } catch (error: unknown) {
138
140
  // Hammering the usage endpoint on every call is its own rate-limit
139
141
  // risk: back off briefly after a 429 instead of retrying into it.
@@ -143,21 +145,16 @@ export async function fetchClaudeQuota(
143
145
  }
144
146
 
145
147
  /**
146
- * Bounded per-credential quota cache (5 min) plus a short 429 cooldown
147
- * (3 min). Keys are credential hashes, never raw secrets; both caches are
148
+ * Short 429 cooldown (3 min). Keys are credential hashes, never raw secrets;
148
149
  * capped so credential fan-out cannot pin memory.
150
+ *
151
+ * The cooldown is an absolute window (`until = now + QUOTA_COOLDOWN_MS`), not a
152
+ * TTL from read time, so it is a sentinel whose own TTL *is* that window: the
153
+ * entry lives for exactly the cooldown and is gone the moment it closes.
149
154
  */
150
- const QUOTA_CACHE_TTL_MS = 5 * 60_000;
151
155
  const QUOTA_COOLDOWN_MS = 3 * 60_000;
152
- const QUOTA_CACHE_MAX_ENTRIES = 256;
153
- const quotaCache = new TtlCache<ProviderQuotaResult>({
154
- ttlMs: QUOTA_CACHE_TTL_MS,
155
- maxEntries: QUOTA_CACHE_MAX_ENTRIES,
156
- });
157
- // The cooldown is an absolute window (`until = now + QUOTA_COOLDOWN_MS`), not a
158
- // TTL from read time, so it is a sentinel whose own TTL *is* that window: the
159
- // entry lives for exactly the cooldown and is gone the moment it closes.
156
+ const QUOTA_COOLDOWN_MAX_ENTRIES = 256;
160
157
  const quotaCooldown = new TtlCache<true>({
161
158
  ttlMs: QUOTA_COOLDOWN_MS,
162
- maxEntries: QUOTA_CACHE_MAX_ENTRIES,
159
+ maxEntries: QUOTA_COOLDOWN_MAX_ENTRIES,
163
160
  });
@@ -8,6 +8,7 @@ import {
8
8
  record,
9
9
  } from "../../authentication/oauth-flow-store";
10
10
  import type { OAuthDeviceFlowContext, OAuthDevicePollResult, OAuthDeviceStartResult, OAuthExchangeResult } from "../../authentication/oauth-flow-store";
11
+ import { oauthAccountIdentity } from "../../authentication/oauth-flow-store";
11
12
  import type { OAuthTokenRefreshResult } from "../../authentication/oauth-refresh-service";
12
13
  import { OAuthDeviceFlow } from "../../authentication/oauth-device-flow";
13
14
 
@@ -45,14 +46,18 @@ function tokenResult(value: unknown, fallbackRefresh?: string): OAuthExchangeRes
45
46
  const refresh =
46
47
  nonEmptyTrimmedString(payload.refresh_token) ?? nonEmptyTrimmedString(payload.refreshToken) ?? fallbackRefresh;
47
48
  if (!access || !refresh) throw new Error("Cline OAuth response omitted access or refresh token");
49
+ const identity = oauthAccountIdentity(value);
48
50
  const accountLabel =
49
51
  nonEmptyTrimmedString(payload.email) ??
50
52
  nonEmptyTrimmedString(record(payload.userInfo)?.email) ??
51
- nonEmptyTrimmedString(record(payload.user)?.email);
53
+ nonEmptyTrimmedString(record(payload.user)?.email) ??
54
+ identity.accountEmail ??
55
+ identity.accountName;
52
56
  return {
53
57
  access,
54
58
  refresh,
55
59
  expiresAt: tokenExpiry(payload),
60
+ ...identity,
56
61
  ...(accountLabel === undefined ? {} : { accountLabel }),
57
62
  };
58
63
  }
@@ -107,8 +112,8 @@ export class ClineOAuthClient extends OAuthDeviceFlow {
107
112
  }
108
113
  }
109
114
 
110
- override async startDeviceAuth(_context?: OAuthDeviceFlowContext): Promise<OAuthDeviceStartResult> {
111
- const response = await this.fetchFn(WORKOS_DEVICE_AUTHORIZATION_URL, {
115
+ override async startDeviceAuth(context?: OAuthDeviceFlowContext): Promise<OAuthDeviceStartResult> {
116
+ const response = await this.fetchFor(context)(WORKOS_DEVICE_AUTHORIZATION_URL, {
112
117
  method: "POST",
113
118
  headers: { "content-type": "application/x-www-form-urlencoded", accept: "application/json" },
114
119
  body: new URLSearchParams({ client_id: CLINE_WORKOS_CLIENT_ID }),
@@ -145,7 +150,7 @@ export class ClineOAuthClient extends OAuthDeviceFlow {
145
150
  this.#sessions.delete(deviceAuthId);
146
151
  return { status: "failed", reason: "Cline device authorization expired" };
147
152
  }
148
- const response = await this.fetchFn(WORKOS_AUTHENTICATE_URL, {
153
+ const response = await this.fetchFor(context)(WORKOS_AUTHENTICATE_URL, {
149
154
  method: "POST",
150
155
  headers: { "content-type": "application/x-www-form-urlencoded", accept: "application/json" },
151
156
  body: new URLSearchParams({
@@ -185,7 +190,7 @@ export class ClineOAuthClient extends OAuthDeviceFlow {
185
190
  // exception that escapes the poll.
186
191
  let registered: Record<string, unknown>;
187
192
  try {
188
- const registerResponse = await this.fetchFn(CLINE_REGISTER_URL, {
193
+ const registerResponse = await this.fetchFor(context)(CLINE_REGISTER_URL, {
189
194
  method: "POST",
190
195
  headers: { "content-type": "application/json", accept: "application/json" },
191
196
  body: JSON.stringify({ accessToken, refreshToken }),
@@ -1,14 +1,14 @@
1
1
  import { createApiKeyAdapter } from "../../configured-provider";
2
2
  import type { ProviderDispatchTarget, ProviderAdapter, ProviderDispatchContext } from "../../provider-contracts";
3
3
  import type { ModelDefinition } from "../../provider-model-definition";
4
- import { unwrapProviderToken } from "../../provider-credential-envelope";
4
+ import { unwrapProviderToken } from "../../provider-credentials";
5
5
  import { GatewayError } from "../../../protocol/gateway-error";
6
6
  import type { CanonicalRequest } from "../../../protocol/canonical-model";
7
7
  import { completeRequiredSchema, isRecord } from "../../../protocol/codecs";
8
8
  import { providerBaseUrl } from "../../provider-metadata";
9
- import { getCachedModelDiscovery } from "../../operations/model-discovery-cache";
9
+ import { getCachedModelDiscovery } from "../../operations/provider-caches";
10
10
  import { defineModel } from "../../provider-model-definition";
11
- import { clineReasoningReplayPrepares } from "../buddy/buddy-chat-shared";
11
+ import { clineReasoningReplayPrepares } from "../buddy/chat";
12
12
  import { clinePlatform, clinePlatformVersion } from "../../operations/cli-platform";
13
13
  import {
14
14
  getClineCliVersion,
@@ -105,7 +105,7 @@ function completeClineToolSchemas(payload: Record<string, unknown>): void {
105
105
  });
106
106
  }
107
107
 
108
- function clinePrePayload(
108
+ export function clinePrePayload(
109
109
  payload: Record<string, unknown>,
110
110
  request: CanonicalRequest,
111
111
  candidate: ProviderDispatchTarget,
@@ -576,11 +576,10 @@ function clineRow(
576
576
  * not a tier marker — membership of the bucket is what makes a row free.
577
577
  */
578
578
  const CLINE_FREE_ROWS: readonly ClineSeedRow[] = [
579
- { id: "reflection/beam-free", ctx: 128_000, out: 32_000 },
580
579
  { id: "cline-free/nemotron-3.5-super-vl", ctx: 1_000_000, out: 131_072, vision: true },
581
580
  { id: "nvidia/nemotron-3.5-lightning-free", ctx: 1_000_000, out: 65_536 },
582
- { id: "poolside/laguna-s-2.1-free", ctx: 262_144, out: 32_768 },
583
581
  { id: "cline-free/solar-mini4", ctx: 524_288, out: 131_072 },
582
+ { id: "baidu/ernie-5.5-flash-preview:free", ctx: 1_000_000, out: 921_600 },
584
583
  ];
585
584
 
586
585
  /**
@@ -0,0 +1,98 @@
1
+ // Clixad credential login (GitHub device flow).
2
+ //
3
+ // Clixad's only production login is a GitHub device flow served by the
4
+ // gateway: `POST /v1/auth/device/start` returns a GitHub verification URL +
5
+ // user code; the gateway polls `POST /v1/auth/device/poll` with the session
6
+ // until the operator approves in the browser, then mints the CLI's bearer
7
+ // token (`adg_...`). There is no refresh token — the token is long-lived and
8
+ // the flow is re-run when needed.
9
+
10
+ import { OAuthClient } from "../../authentication/oauth-client";
11
+ import type {
12
+ OAuthDeviceStartResult,
13
+ OAuthDevicePollResult,
14
+ OAuthDeviceFlowContext,
15
+ } from "../../authentication/oauth-flow-store";
16
+
17
+ const CLIXAD_HANDOFF_PATH = "/v1/auth/device/start";
18
+ const CLIXAD_POLL_PATH = "/v1/auth/device/poll";
19
+ const CLIXAD_BASE_URL = "https://clixad.onrender.com";
20
+
21
+ export class ClixadOAuthClient extends OAuthClient {
22
+ readonly supportsDeviceCode = true;
23
+ readonly supportsBrowserCode = false;
24
+ protected readonly providerLabel = "clixad";
25
+ protected readonly clientId = "";
26
+ protected readonly tokenUrl = "";
27
+ protected readonly scopes = "";
28
+
29
+ async startDeviceAuth(): Promise<OAuthDeviceStartResult> {
30
+ const res = await this.fetchFn(new URL(CLIXAD_HANDOFF_PATH, CLIXAD_BASE_URL), {
31
+ method: "POST",
32
+ headers: { "content-type": "application/json", accept: "application/json" },
33
+ });
34
+ if (!res.ok) {
35
+ const text = await res.text().catch(() => "");
36
+ throw new Error(`clixad device flow start failed: HTTP ${res.status} ${text.slice(0, 300)}`);
37
+ }
38
+ const body = (await res.json()) as Record<string, unknown>;
39
+ return {
40
+ verificationUri: String(body["verification_uri"] ?? ""),
41
+ userCode: String(body["user_code"] ?? ""),
42
+ deviceAuthId: String(body["session"] ?? ""),
43
+ intervalSeconds: Number(body["interval"] ?? 5),
44
+ expiresInSeconds: Number(body["expires_in"] ?? 900),
45
+ providerState: JSON.stringify({ session: body["session"] }),
46
+ };
47
+ }
48
+
49
+ async pollDeviceAuth(deviceAuthId: string, context?: OAuthDeviceFlowContext): Promise<OAuthDevicePollResult> {
50
+ let providerState: Record<string, unknown> = {};
51
+ try {
52
+ providerState = JSON.parse(context?.providerState ?? "{}") as Record<string, unknown>;
53
+ } catch {
54
+ /* ignore */
55
+ }
56
+ const session = String(providerState["session"] ?? deviceAuthId);
57
+ const res = await this.fetchFn(new URL(CLIXAD_POLL_PATH, CLIXAD_BASE_URL), {
58
+ method: "POST",
59
+ headers: { "content-type": "application/json", accept: "application/json" },
60
+ body: JSON.stringify({ session }),
61
+ });
62
+ const body = (await res.json().catch(() => ({}))) as Record<string, unknown>;
63
+ const status = String(body["status"] ?? "");
64
+ if (status === "complete") {
65
+ const token = String(body["token"] ?? "");
66
+ return {
67
+ status: "complete",
68
+ result: {
69
+ access: token,
70
+ // Clixad issues no refresh token — the bearer is long-lived and the
71
+ // flow is re-run when needed. Mirror the access token so the console
72
+ // store always has a non-empty refresh field.
73
+ refresh: token,
74
+ expiresAt: new Date(Date.now() + 365 * 24 * 60 * 60 * 1000),
75
+ accountLabel: String(body["email"] ?? body["login"] ?? ""),
76
+ auth_state: {
77
+ userId: body["userId"],
78
+ email: body["email"],
79
+ login: body["login"],
80
+ defaultModel: body["default_model"],
81
+ },
82
+ },
83
+ };
84
+ }
85
+ if (status === "closed") {
86
+ return {
87
+ status: "failed",
88
+ reason: `signup closed: ${String(body["message"] ?? "")} (${String(body["waitlist_url"] ?? "")})`,
89
+ };
90
+ }
91
+ if (status === "pending") return { status: "pending" };
92
+ if (status === "unavailable") return { status: "failed", reason: `unavailable: ${String(body["error"] ?? "")}` };
93
+ return { status: "failed", reason: `device poll failed: ${String(body["error"] ?? body["status"] ?? res.status)}` };
94
+ }
95
+ }
96
+
97
+ /** Shared Clixad device-login client instance (registry-facing). */
98
+ export const clixadOAuthClient = new ClixadOAuthClient();
@@ -0,0 +1,132 @@
1
+ // Clixad quota collector.
2
+ //
3
+ // Reads the account's credit surface from the gateway's wallet endpoint
4
+ // (`GET /v1/wallet` with the stored bearer token). The wallet reports the
5
+ // billed balance, the daily free-tier allowance (`free_tier.daily_credits` /
6
+ // `credits_left` — resets at midnight UTC), turns left per free model, and
7
+ // the ad-reward earnings cap (`max_reward_usd_per_day`). Reads are
8
+ // Bearer-authenticated; never throws — a failed read returns a null result so
9
+ // the router treats it as unknown rather than dead.
10
+
11
+ import type { FetchLike, ProviderQuotaResult, ProviderQuotaWindow } from "../../quota/quota-contracts";
12
+ import { cleanError, number, percentWindow, text } from "../../quota/quota-contracts";
13
+ import type { QuotaCollectionContext } from "../../quota/quota-support";
14
+
15
+ async function readWallet(
16
+ baseUrl: string,
17
+ token: string,
18
+ fetcher: FetchLike,
19
+ ): Promise<Record<string, unknown> | null> {
20
+ if (!token) return null;
21
+ return fetcher(`${baseUrl}/v1/wallet`, {
22
+ method: "GET",
23
+ headers: {
24
+ accept: "application/json",
25
+ authorization: `Bearer ${token}`,
26
+ },
27
+ signal: AbortSignal.timeout(15_000),
28
+ })
29
+ .then(async (res) => {
30
+ if (!res.ok) return null;
31
+ try {
32
+ return (await res.json()) as Record<string, unknown>;
33
+ } catch {
34
+ return null;
35
+ }
36
+ })
37
+ .catch(() => null);
38
+ }
39
+
40
+ /** Next midnight UTC, for the free-tier reset window. */
41
+ function nextMidnightUtc(): string {
42
+ const now = new Date();
43
+ const next = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate() + 1));
44
+ return next.toISOString();
45
+ }
46
+
47
+ /**
48
+ * Reads the account quota and returns a normalized result.
49
+ *
50
+ * Pooled windows: the billed balance (no ceiling — absolute remaining only)
51
+ * and the daily free-tier allowance (120k credits/day, resets at midnight
52
+ * UTC). Independent window: the ad-reward earnings cap ($5/day).
53
+ */
54
+ export async function fetchClixadQuota(
55
+ credential: string,
56
+ fetcher: FetchLike,
57
+ _context?: QuotaCollectionContext,
58
+ ): Promise<ProviderQuotaResult> {
59
+ try {
60
+ const token = text(credential) ?? "";
61
+ const wallet = await readWallet("https://clixad.onrender.com", token, fetcher);
62
+ if (!wallet) {
63
+ return { source: "clixad", plan: null, windows: [], error: "Clixad wallet endpoint unavailable" };
64
+ }
65
+ const windows: ProviderQuotaWindow[] = [];
66
+
67
+ const balance = number(wallet["balance"]);
68
+ if (balance !== null) {
69
+ // Billed balance: pooled, absolute remaining, no ceiling (a bonus pack
70
+ // has no declared limit — it simply is what it is).
71
+ windows.push({
72
+ kind: "credits",
73
+ label: "Account credits",
74
+ accounting: "pooled",
75
+ usedPercent: null,
76
+ remainingPercent: null,
77
+ resetsAt: null,
78
+ remaining: Math.max(0, balance),
79
+ recurring: false,
80
+ });
81
+ }
82
+
83
+ const freeTier = wallet["free_tier"] as Record<string, unknown> | undefined;
84
+ const dailyCredits = freeTier === undefined ? null : number(freeTier["daily_credits"]);
85
+ if (dailyCredits !== null && dailyCredits > 0) {
86
+ const creditsLeft = freeTier === undefined ? null : number(freeTier["credits_left"]);
87
+ const used = creditsLeft === null ? null : Math.max(0, dailyCredits - creditsLeft);
88
+ // Daily free tier: pooled (it is credit the account can spend), ceiling
89
+ // = the daily allowance, resets at midnight UTC.
90
+ windows.push(
91
+ percentWindow(
92
+ "free_daily",
93
+ "Daily free tier",
94
+ used === null ? null : (used / dailyCredits) * 100,
95
+ nextMidnightUtc(),
96
+ used,
97
+ dailyCredits,
98
+ "pooled",
99
+ ),
100
+ );
101
+ }
102
+
103
+ const maxRewardUsd = number(wallet["max_reward_usd_per_day"]);
104
+ if (maxRewardUsd !== null && maxRewardUsd > 0) {
105
+ const earnedUsd = number(wallet["earned_usd_today"]) ?? 0;
106
+ const usedUsd = Math.min(maxRewardUsd, Math.max(0, earnedUsd));
107
+ // Ad-reward earnings cap: independent (it does not fund model calls
108
+ // directly — it caps how much *earning* can happen per day).
109
+ windows.push(
110
+ percentWindow(
111
+ "earn_usd_daily",
112
+ "Daily reward cap",
113
+ (usedUsd / maxRewardUsd) * 100,
114
+ nextMidnightUtc(),
115
+ usedUsd,
116
+ maxRewardUsd,
117
+ "independent",
118
+ ),
119
+ );
120
+ }
121
+
122
+ const plan = number(wallet["balance"]) !== null ? "metered" : null;
123
+ return {
124
+ source: "clixad",
125
+ plan,
126
+ windows,
127
+ error: null,
128
+ };
129
+ } catch (error) {
130
+ return { source: "clixad", plan: null, windows: [], error: cleanError(error) };
131
+ }
132
+ }
@@ -0,0 +1,145 @@
1
+ // Clixad provider — TypeScript, Clixad gateway, Bun gateway.
2
+ //
3
+ // Clixad is an ad-funded terminal coding agent whose gateway
4
+ // (`https://clixad.onrender.com`) meters model calls and keeps the account
5
+ // balance, free-tier allowance, and ad-reward network server-side. The chat
6
+ // surface is OpenAI-compatible (`/v1/chat/completions`) with a bearer token
7
+ // (`adg_...`) minted by the GitHub device flow at login; model calls report
8
+ // credit accounting in an extra `clixad` envelope.
9
+ //
10
+ // Verified live 2026-11-11 (evidence ledger:
11
+ // `documentation/research/clixad/2026-11-11-clixad-wire.md`):
12
+ // - GET /v1/models → 200, 14 rows; the five `free: true` rows are
13
+ // served on the daily free tier (120k credits/day) and are the ones seeded
14
+ // below. The remaining rows are billed against the account balance.
15
+ // - POST /v1/chat/completions → 200 stream for all five free models, with
16
+ // `clixad.credits_waived` accounting the free-tier draw.
17
+ // - GET /v1/wallet → balance + free_tier (daily allowance, turns
18
+ // left per model), the quota source.
19
+ //
20
+ // The static roster carries the five live-verified free rows so the Free
21
+ // Limited section is populated before any fetch. Live discovery keeps the list
22
+ // current; the roster rows are preserved when the upstream listing reports the
23
+ // same id (the shared reader would otherwise overwrite freeTier/reasoning/tool
24
+ // flags with its conservative defaults).
25
+
26
+ import type { ApiKeyProviderSpec } from "../../configured-provider";
27
+ import { fetchOpenAICompatibleModels } from "../../discovery/openai-model-discovery";
28
+ import {
29
+ defineModel,
30
+ type ModelDefinition,
31
+ } from "../../provider-model-definition";
32
+
33
+ const CLIXAD_PROVIDER_ID = "clixad" as const;
34
+
35
+ /** Chat path below the `/v1` base root. */
36
+ const CLIXAD_CHAT_PATH = "/chat/completions";
37
+
38
+ /**
39
+ * Clixad adapter spec: plain bearer, OpenAI-compatible chat.
40
+ *
41
+ * The model call endpoint accepts standard OpenAI fields and streams normally,
42
+ * so the generic API-key adapter serves it. No bespoke headers are required
43
+ * beyond the bearer. (The upstream's gzipped-body fallback is a *client-side*
44
+ * workaround for an edge WAF; the gateway itself answers plain JSON.)
45
+ */
46
+ export const CLIXAD_SPEC: ApiKeyProviderSpec = {
47
+ provider_id: CLIXAD_PROVIDER_ID,
48
+ endpoint_paths_by_wire_family: { chat: CLIXAD_CHAT_PATH },
49
+ };
50
+
51
+ /**
52
+ * Default rows for the Clixad provider (2026-11-11).
53
+ *
54
+ * Every row below was verified live on a fresh account: it streams a reply and
55
+ * draws from the daily free tier (`credits_waived` per call). Context windows
56
+ * and effort ladders come from the upstream's own `/v1/models` listing; token
57
+ * prices are the credits-per-1M figures the upstream publishes, kept here as
58
+ * `cost` for the operator (the tier itself bills zero — `FREE_TIER_COST`).
59
+ */
60
+ export const CLIXAD_DEFAULT_MODELS: readonly ModelDefinition[] = [
61
+ defineModel({
62
+ id: "gpt-5-nano",
63
+ wireFamily: "chat",
64
+ endpoint: CLIXAD_CHAT_PATH,
65
+ ctx: 400_000,
66
+ out: 60_000,
67
+ reasoning: true,
68
+ toolCall: true,
69
+ free: true,
70
+ freeTier: true,
71
+ reasoningEfforts: ["minimal", "low", "medium", "high"]
72
+ }),
73
+ defineModel({
74
+ id: "gemini-2.5-flash-lite",
75
+ wireFamily: "chat",
76
+ endpoint: CLIXAD_CHAT_PATH,
77
+ ctx: 1_048_576,
78
+ out: 60_000,
79
+ reasoning: true,
80
+ toolCall: true,
81
+ free: true,
82
+ freeTier: true,
83
+ reasoningEfforts: ["off", "low", "medium", "high"]
84
+ }),
85
+ defineModel({
86
+ id: "claude-haiku-5.5",
87
+ wireFamily: "chat",
88
+ endpoint: CLIXAD_CHAT_PATH,
89
+ ctx: 24_000,
90
+ // The gateway publishes no output cap; clamp under the 24k context so a
91
+ // request can never exceed the endpoint (input + output budget).
92
+ out: 16_000,
93
+ reasoning: true,
94
+ toolCall: true,
95
+ free: true,
96
+ freeTier: true,
97
+ reasoningEfforts: ["off", "low", "medium", "high", "xhigh", "max"]
98
+ }),
99
+ defineModel({
100
+ id: "deepseek-v4-flash",
101
+ wireFamily: "chat",
102
+ endpoint: CLIXAD_CHAT_PATH,
103
+ ctx: 1_048_576,
104
+ out: 42_000,
105
+ reasoning: true,
106
+ toolCall: true,
107
+ free: true,
108
+ freeTier: true,
109
+ reasoningEfforts: ["off", "low", "medium", "high", "xhigh", "max"]
110
+ }),
111
+ defineModel({
112
+ id: "gemini-3.5-flash-lite",
113
+ wireFamily: "chat",
114
+ endpoint: CLIXAD_CHAT_PATH,
115
+ ctx: 1_048_576,
116
+ out: 375_000,
117
+ reasoning: true,
118
+ toolCall: true,
119
+ free: true,
120
+ freeTier: true,
121
+ reasoningEfforts: ["off", "low", "medium", "high", "xhigh", "max"]
122
+ }),
123
+ ];
124
+
125
+ /**
126
+ * Live `/v1/models` listing.
127
+ *
128
+ * The listing is unauthenticated (verified live). Every fetched row is kept
129
+ * as the shared reader resolved it, except ids the static roster already knows
130
+ * — those keep the roster row whole so the freeTier/reasoning/tool flags are
131
+ * not overwritten by the listing's conservative defaults.
132
+ */
133
+ export async function discoverClixadModels(options: {
134
+ readonly baseUrl: string;
135
+ readonly fetcher?: typeof fetch;
136
+ }): Promise<readonly ModelDefinition[] | null> {
137
+ const listed = await fetchOpenAICompatibleModels({
138
+ baseUrl: options.baseUrl,
139
+ providerId: "clixad",
140
+ ...(options.fetcher === undefined ? {} : { fetcher: options.fetcher }),
141
+ });
142
+ if (listed === null) return null;
143
+ const known = new Map(CLIXAD_DEFAULT_MODELS.map((model) => [model.modelId, model]));
144
+ return listed.map((model) => known.get(model.modelId) ?? model);
145
+ }
@@ -3,6 +3,8 @@ import {
3
3
  postFormTokenRequest,
4
4
  InMemoryOAuthFlowStore,
5
5
  OAuthFlowStore,
6
+ type OAuthCodeExchangeContext,
7
+ type OAuthDeviceFlowContext,
6
8
  type OAuthDevicePollResult,
7
9
  type OAuthDeviceStartResult,
8
10
  type OAuthExchangeResult,
@@ -200,12 +202,13 @@ export class CodexOAuthClient extends OAuthDeviceFlow {
200
202
  codeVerifier: string,
201
203
  redirectUri: string,
202
204
  _state?: string,
205
+ _context?: OAuthCodeExchangeContext,
203
206
  ): Promise<OAuthExchangeResult> {
204
207
  return exchangeCodeForToken(code, codeVerifier, redirectUri, this.fetchFn);
205
208
  }
206
209
 
207
- override async startDeviceAuth(): Promise<OAuthDeviceStartResult> {
208
- const started = await startCodexDeviceAuth(CODEX_CLIENT_ID, this.fetchFn);
210
+ override async startDeviceAuth(context?: OAuthDeviceFlowContext): Promise<OAuthDeviceStartResult> {
211
+ const started = await startCodexDeviceAuth(CODEX_CLIENT_ID, this.fetchFor(context));
209
212
  await this.#requireStore().saveDeviceState(started.deviceAuthId, { ...started });
210
213
  return {
211
214
  verificationUri: "https://auth.openai.com/codex/device",
@@ -216,7 +219,7 @@ export class CodexOAuthClient extends OAuthDeviceFlow {
216
219
  };
217
220
  }
218
221
 
219
- override async pollDeviceAuth(deviceAuthId: string): Promise<OAuthDevicePollResult> {
222
+ override async pollDeviceAuth(deviceAuthId: string, context?: OAuthDeviceFlowContext): Promise<OAuthDevicePollResult> {
220
223
  const store = this.#requireStore();
221
224
  const stored = await store.getDeviceState(deviceAuthId);
222
225
  const device = stored === undefined ? undefined : parseCodexDeviceStart(stored);
@@ -227,7 +230,7 @@ export class CodexOAuthClient extends OAuthDeviceFlow {
227
230
  // never an exception that escapes the dashboard's poll request.
228
231
  let authorization;
229
232
  try {
230
- authorization = await pollCodexDeviceAuth(device, this.fetchFn);
233
+ authorization = await pollCodexDeviceAuth(device, this.fetchFor(context));
231
234
  } catch (error) {
232
235
  return { status: "failed", reason: error instanceof Error ? error.message : "Codex device polling failed" };
233
236
  }
@@ -238,7 +241,7 @@ export class CodexOAuthClient extends OAuthDeviceFlow {
238
241
  authorization.authorizationCode,
239
242
  authorization.codeVerifier,
240
243
  CODEX_DEVICE_REDIRECT_URI,
241
- this.fetchFn,
244
+ this.fetchFor(context),
242
245
  );
243
246
  } catch (error) {
244
247
  return { status: "failed", reason: error instanceof Error ? error.message : "Codex token exchange failed" };