cartethyia 2.8.0 → 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 (348) hide show
  1. package/.env.example +3 -3
  2. package/dist/dashboard/assets/{BannedPage-Cc0vKpMG.js → BannedPage-Dq530awD.js} +1 -1
  3. package/dist/dashboard/assets/BannedPage-Dq530awD.js.gz +0 -0
  4. package/dist/dashboard/assets/{CliToolDetailPage-Bqj2aa1T.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-CXsaOAO2.js → CliToolsPage-DaZZ3cqP.js} +1 -1
  7. package/dist/dashboard/assets/CliToolsPage-DaZZ3cqP.js.gz +0 -0
  8. package/dist/dashboard/assets/{CombosPage-BeLpJ4ma.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-DnV0uZ3m.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-CBxAjo63.js → CustomizationPage-7xT5yg2E.js} +1 -1
  13. package/dist/dashboard/assets/CustomizationPage-7xT5yg2E.js.gz +0 -0
  14. package/dist/dashboard/assets/{HealthEventsModal-B5yEE0k6.js → HealthEventsModal-Clq8E9cW.js} +1 -1
  15. package/dist/dashboard/assets/HealthEventsModal-Clq8E9cW.js.gz +0 -0
  16. package/dist/dashboard/assets/{InjectSystemPromptPanel-BWFLECmz.js → InjectSystemPromptPanel-6Teoq9Se.js} +1 -1
  17. package/dist/dashboard/assets/InjectSystemPromptPanel-6Teoq9Se.js.gz +0 -0
  18. package/dist/dashboard/assets/{LoginPage-CgoPc5B9.js → LoginPage-CZuI0MAR.js} +1 -1
  19. package/dist/dashboard/assets/LoginPage-CZuI0MAR.js.gz +0 -0
  20. package/dist/dashboard/assets/{ModelPicker-D9bFQern.js → ModelPicker-C1hNTlkr.js} +1 -1
  21. package/dist/dashboard/assets/ModelPicker-C1hNTlkr.js.gz +0 -0
  22. package/dist/dashboard/assets/{OverviewPage-B_p4tL8k.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-D_0PiZtg.js → ProviderIcon-Ddhm1InL.js} +1 -1
  27. package/dist/dashboard/assets/ProviderIcon-Ddhm1InL.js.gz +0 -0
  28. package/dist/dashboard/assets/{ProvidersPage-CLI2bJTK.js → ProvidersPage-C3fYuu6i.js} +1 -1
  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-DA-a1iSN.js → QuotaPage-D1RV_2BX.js} +1 -1
  33. package/dist/dashboard/assets/QuotaPage-D1RV_2BX.js.gz +0 -0
  34. package/dist/dashboard/assets/{SettingsPage-CctTga2C.js → SettingsPage-BzZAvYSX.js} +1 -1
  35. package/dist/dashboard/assets/SettingsPage-BzZAvYSX.js.gz +0 -0
  36. package/dist/dashboard/assets/{SetupPage-febLU4r1.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-M6fvR5Wz.js → StudioPage-BjXFGSK4.js} +1 -1
  39. package/dist/dashboard/assets/StudioPage-BjXFGSK4.js.gz +0 -0
  40. package/dist/dashboard/assets/{UsagePage-nyeuRqz9.js → UsagePage-CP8F8BN7.js} +1 -1
  41. package/dist/dashboard/assets/UsagePage-CP8F8BN7.js.gz +0 -0
  42. package/dist/dashboard/assets/{api-keys-BFOlvrJC.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-CqTLw5e7.js → assert-BMcj1xZE.js} +1 -1
  47. package/dist/dashboard/assets/assert-BMcj1xZE.js.gz +0 -0
  48. package/dist/dashboard/assets/{badge-C83EqLB0.js → badge-DkRUuVfz.js} +1 -1
  49. package/dist/dashboard/assets/badge-DkRUuVfz.js.gz +0 -0
  50. package/dist/dashboard/assets/{banner-D1xn7pIe.js → banner-CDjW841Q.js} +1 -1
  51. package/dist/dashboard/assets/banner-CDjW841Q.js.gz +0 -0
  52. package/dist/dashboard/assets/{card-DU3Lq5Lm.js → card-Bto5UlL9.js} +1 -1
  53. package/dist/dashboard/assets/card-Bto5UlL9.js.gz +0 -0
  54. package/dist/dashboard/assets/{cli-tools-Dw1U_drK.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-CEL5Sg_J.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-BjFR5UHy.js → dialog-DL0Zftwv.js} +1 -1
  59. package/dist/dashboard/assets/dialog-DL0Zftwv.js.gz +0 -0
  60. package/dist/dashboard/assets/{drawer-HvBW0AjI.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-CECVELyy.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-DYYofQ7W.js → entry-CGvdNU6r.js} +1 -1
  65. package/dist/dashboard/assets/entry-CGvdNU6r.js.gz +0 -0
  66. package/dist/dashboard/assets/{entry-BSu3gFlt.js → entry-DScKYvZj.js} +3 -3
  67. package/dist/dashboard/assets/entry-DScKYvZj.js.gz +0 -0
  68. package/dist/dashboard/assets/{formatters-DLW4i8zK.js → formatters-BS3DZlZj.js} +1 -1
  69. package/dist/dashboard/assets/formatters-BS3DZlZj.js.gz +0 -0
  70. package/dist/dashboard/assets/{index-1-fCqe64.js → index-SQnvvTWX.js} +2 -2
  71. package/dist/dashboard/assets/index-SQnvvTWX.js.gz +0 -0
  72. package/dist/dashboard/assets/{inline-DGvU2oLK.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-iNnO1X2K.js → input-Bfx0EzRc.js} +1 -1
  75. package/dist/dashboard/assets/input-Bfx0EzRc.js.gz +0 -0
  76. package/dist/dashboard/assets/{layout-0q8UGypb.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-C0w2bQjJ.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-M5aJ_xCa.js → live-CbVTdX9V.js} +1 -1
  81. package/dist/dashboard/assets/live-CbVTdX9V.js.gz +0 -0
  82. package/dist/dashboard/assets/{network-BKeQGT3K.js → network-JGvDveof.js} +1 -1
  83. package/dist/dashboard/assets/network-JGvDveof.js.gz +0 -0
  84. package/dist/dashboard/assets/{numbers-CivAk3da.js → numbers-Dw6kiew-.js} +1 -1
  85. package/dist/dashboard/assets/numbers-Dw6kiew-.js.gz +0 -0
  86. package/dist/dashboard/assets/{pagination-CpzvI5ep.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/{routing-BFQX2VBc.js → routing-8jZr-3WE.js} +1 -1
  91. package/dist/dashboard/assets/routing-8jZr-3WE.js.gz +0 -0
  92. package/dist/dashboard/assets/{select-CXbck0jG.js → select-oO1FxMVb.js} +1 -1
  93. package/dist/dashboard/assets/select-oO1FxMVb.js.gz +0 -0
  94. package/dist/dashboard/assets/{settings-C_XvMHh9.js → settings-CLr3kdIx.js} +1 -1
  95. package/dist/dashboard/assets/settings-CLr3kdIx.js.gz +0 -0
  96. package/dist/dashboard/assets/{stack-DggRiCxE.js → stack-Y3pDteZr.js} +1 -1
  97. package/dist/dashboard/assets/stack-Y3pDteZr.js.gz +0 -0
  98. package/dist/dashboard/assets/{state-ClFzfbYp.js → state-CArX_Iak.js} +1 -1
  99. package/dist/dashboard/assets/state-CArX_Iak.js.gz +0 -0
  100. package/dist/dashboard/assets/{switch-CTIFxKqS.js → switch-DwlwmDO-.js} +1 -1
  101. package/dist/dashboard/assets/switch-DwlwmDO-.js.gz +0 -0
  102. package/dist/dashboard/assets/{useMutation-BClG3yWj.js → useMutation-DJ9GYEgf.js} +1 -1
  103. package/dist/dashboard/assets/useMutation-DJ9GYEgf.js.gz +0 -0
  104. package/dist/dashboard/index.html +2 -2
  105. package/dist/dashboard/index.html.gz +0 -0
  106. package/dist/dashboard/precompressed-assets.json +51 -51
  107. package/migrations/full-postgres/0047_add_provider_device_flow_proxy.sql +17 -0
  108. package/migrations/lite-sqlite3/0047_add_provider_device_flow_proxy.sql +19 -0
  109. package/package.json +1 -1
  110. package/src/boot/app.ts +1 -1
  111. package/src/boot/dependencies.ts +4 -6
  112. package/src/config/providers.ts +6 -5
  113. package/src/config/spec.ts +8 -8
  114. package/src/console/backup/nine-router.ts +71 -10
  115. package/src/console/backup/service.ts +8 -1
  116. package/src/console/console-router.ts +1 -1
  117. package/src/console/domain-registration.ts +7 -2
  118. package/src/console/inferhub/client.ts +1 -1
  119. package/src/console/observability/live.ts +2 -2
  120. package/src/console/providers/catalog/contracts.ts +9 -3
  121. package/src/console/providers/catalog/model-operations.ts +2 -3
  122. package/src/console/providers/catalog/provider-operations.ts +1 -1
  123. package/src/console/providers/catalog/routes.ts +2 -2
  124. package/src/console/providers/catalog/store.ts +1914 -1907
  125. package/src/console/providers/detail/routes.ts +1 -0
  126. package/src/console/providers/detail/store.ts +4 -0
  127. package/src/console/providers/oauth/callback-listener.ts +30 -11
  128. package/src/console/providers/oauth/device-egress.ts +123 -0
  129. package/src/console/providers/oauth/routes.ts +123 -16
  130. package/src/console/quota/account-quota-tenant.ts +5 -5
  131. package/src/console/quota/account-test-egress.ts +2 -2
  132. package/src/console/quota/daily-growth-job.ts +1 -1
  133. package/src/console/routing/pools/contracts.ts +1 -1
  134. package/src/console/routing/pools/operations.ts +1 -1
  135. package/src/console/routing/pools/probe-verdict.ts +2 -2
  136. package/src/console/routing/pools/relay-deploy.ts +1 -1
  137. package/src/console/routing/pools/store.ts +4 -4
  138. package/src/network/client.ts +67 -0
  139. package/src/network/egress/bidi-session.ts +322 -0
  140. package/src/network/egress/dial.ts +201 -0
  141. package/src/network/egress/factory.ts +191 -0
  142. package/src/network/{outbound-fetch.ts → egress/fetch.ts} +195 -29
  143. package/src/network/egress/tls-profile.ts +123 -0
  144. package/src/network/egress/ws.ts +175 -0
  145. package/src/network/{types.ts → pool/agent-config.ts} +1 -1
  146. package/src/network/pool/agent.ts +2 -2
  147. package/src/network/{pool-cooldown-store.ts → pool/cooldown-store.ts} +7 -7
  148. package/src/network/{pool-health-machine.ts → pool/health.ts} +205 -6
  149. package/src/network/pool/resolver.ts +10 -849
  150. package/src/network/pool/selector.ts +2 -2
  151. package/src/network/ssrf.ts +1 -1
  152. package/src/network/{retry.ts → util/retry.ts} +1 -1
  153. package/src/observability/telemetry-buffer.ts +1 -1
  154. package/src/persistence/schema.ts +9 -0
  155. package/src/persistence/ttl-cache.ts +1 -1
  156. package/src/protocol/codecs.ts +58 -1
  157. package/src/protocol/request/messages.ts +31 -18
  158. package/src/protocol/response/messages.ts +34 -7
  159. package/src/protocol/translation/quirks.ts +15 -0
  160. package/src/protocol/upstream/send-claude-messages.ts +2 -2
  161. package/src/providers/adapters/compatible-adapter.ts +5 -0
  162. package/src/providers/authentication/oauth-client.ts +40 -6
  163. package/src/providers/authentication/oauth-device-flow.ts +7 -3
  164. package/src/providers/authentication/oauth-flow-store.ts +82 -1
  165. package/src/providers/authentication/oauth-refresh-service.ts +766 -739
  166. package/src/providers/default-registry.ts +76 -0
  167. package/src/providers/discovery/discovery-types.ts +7 -0
  168. package/src/providers/discovery/probe-phases.ts +6 -2
  169. package/src/providers/discovery/probing-service.ts +7 -0
  170. package/src/providers/integrations/antigravity/antigravity-protocol.ts +11 -0
  171. package/src/providers/integrations/buddy/account.ts +969 -0
  172. package/src/providers/integrations/buddy/capabilities.ts +10 -10
  173. package/src/providers/integrations/buddy/{buddy-catalog-shared.ts → catalog.ts} +198 -136
  174. package/src/providers/integrations/buddy/{buddy-chat-shared.ts → chat.ts} +5 -5
  175. package/src/providers/integrations/buddy/codebuddy/index.ts +429 -0
  176. package/src/providers/integrations/buddy/codebuddy/shared.ts +4 -4
  177. package/src/providers/integrations/buddy/{buddy-identity.ts → headers.ts} +138 -140
  178. package/src/providers/integrations/buddy/lifecycle.ts +1018 -0
  179. package/src/providers/integrations/buddy/{buddy-media.ts → media.ts} +1 -1
  180. package/src/providers/integrations/cline/cline-oauth.ts +10 -5
  181. package/src/providers/integrations/cline/cline.ts +3 -4
  182. package/src/providers/integrations/clixad/clixad-oauth.ts +98 -0
  183. package/src/providers/integrations/clixad/clixad-quota.ts +132 -0
  184. package/src/providers/integrations/clixad/provider.ts +145 -0
  185. package/src/providers/integrations/codex/codex-oauth.ts +8 -5
  186. package/src/providers/integrations/cursor/cursor-auth.ts +1 -1
  187. package/src/providers/integrations/cursor/cursor-hardening.ts +279 -0
  188. package/src/providers/integrations/cursor/cursor-http2.ts +605 -6
  189. package/src/providers/integrations/cursor/cursor-shared.ts +1 -1
  190. package/src/providers/integrations/cursor/cursor.ts +485 -70
  191. package/src/providers/integrations/deepai/deepai-catalog.ts +149 -0
  192. package/src/providers/integrations/deepai/deepai-common.ts +145 -0
  193. package/src/providers/integrations/deepai/deepai-oauth.ts +129 -0
  194. package/src/providers/integrations/deepai/deepai-quota.ts +97 -0
  195. package/src/providers/integrations/deepai/provider.ts +42 -0
  196. package/src/providers/integrations/devin/devin-auth.ts +7 -0
  197. package/src/providers/integrations/factory/factory-oauth.ts +16 -7
  198. package/src/providers/integrations/freebuff/freebuff.ts +2 -2
  199. package/src/providers/integrations/freepi/freepi-catalog.ts +5 -0
  200. package/src/providers/integrations/freepi/freepi-discovery.ts +2 -2
  201. package/src/providers/integrations/freepi/freepi-headers.ts +2 -2
  202. package/src/providers/integrations/freepi/freepi-protocol.ts +11 -2
  203. package/src/providers/integrations/freepi/freepi-realm.ts +0 -3
  204. package/src/providers/integrations/freepi/freepi-session.ts +158 -0
  205. package/src/providers/integrations/freepi/freepi.ts +80 -54
  206. package/src/providers/integrations/github/github-discovery.ts +30 -13
  207. package/src/providers/integrations/github/github-oauth.ts +217 -44
  208. package/src/providers/integrations/github/github-quota.ts +22 -5
  209. package/src/providers/integrations/github/github.ts +40 -32
  210. package/src/providers/integrations/grok/grok-oauth.ts +4 -4
  211. package/src/providers/integrations/hermes/hermes-oauth.ts +3 -3
  212. package/src/providers/integrations/hermes/hermes.ts +34 -5
  213. package/src/providers/integrations/kilo/kilo-oauth.ts +4 -4
  214. package/src/providers/integrations/kilo/kilo.ts +1 -1
  215. package/src/providers/integrations/kimi/kimi-oauth.ts +3 -3
  216. package/src/providers/integrations/kiro/kiro-oauth.ts +2 -2
  217. package/src/providers/integrations/muse/muse-oauth.ts +5 -5
  218. package/src/providers/integrations/opencode/fingerprint.ts +1 -1
  219. package/src/providers/integrations/openrouter/provider.ts +1 -1
  220. package/src/providers/integrations/perch/perch-protocol.ts +0 -3
  221. package/src/providers/integrations/perch/perch.ts +3 -3
  222. package/src/providers/integrations/qoder/claim-qoder-credits.ts +1 -1
  223. package/src/providers/integrations/qoder/qoder-adapter.ts +19 -113
  224. package/src/providers/integrations/qoder/qoder-auth.ts +8 -1
  225. package/src/providers/integrations/qoder/qoder-catalog.ts +1 -1
  226. package/src/providers/integrations/qoder/qoder-chat.ts +25 -8
  227. package/src/providers/integrations/qoder/qoder-checkin.ts +1 -1
  228. package/src/providers/integrations/qoder/qoder-cosy.ts +37 -9
  229. package/src/providers/integrations/qoder/qoder-oauth.ts +9 -2
  230. package/src/providers/integrations/qoder/qoder-realm.ts +13 -36
  231. package/src/providers/integrations/qoder/qoder-sse.ts +447 -68
  232. package/src/providers/integrations/qoder/qoder-types.ts +9 -0
  233. package/src/providers/integrations/qoder/qoder.ts +3 -25
  234. package/src/providers/integrations/sensenova/provider.ts +144 -0
  235. package/src/providers/integrations/trae/trae-quota.ts +15 -1
  236. package/src/providers/integrations/vercel/provider.ts +117 -0
  237. package/src/providers/integrations/vercel/vercel-quota.ts +96 -0
  238. package/src/providers/integrations/xai/xai-oauth.ts +5 -5
  239. package/src/providers/integrations/zcode-start-plan/gifts.ts +1 -1
  240. package/src/providers/integrations/zcode-start-plan/oauth.ts +3 -3
  241. package/src/providers/integrations/zro/zro.ts +2 -1
  242. package/src/providers/operations/account-refresh-status.ts +14 -0
  243. package/src/providers/operations/client-versions.ts +95 -30
  244. package/src/providers/operations/oauth-refresh-lead.ts +46 -22
  245. package/src/providers/operations/provider-caches.ts +1 -1
  246. package/src/providers/operations/provider-credential-service.ts +22 -25
  247. package/src/providers/operations/session-resolution.ts +17 -1
  248. package/src/providers/provider-contracts.ts +2 -2
  249. package/src/providers/provider-metadata.ts +30 -0
  250. package/src/providers/quota/quota-contracts.ts +2 -0
  251. package/src/providers/quota/refresh.ts +25 -3
  252. package/src/security/ip-boundary.ts +1 -1
  253. package/src/transport/dispatch/attempt-finalize.ts +1 -1
  254. package/src/transport/dispatch/attempt-loop.ts +3 -3
  255. package/src/transport/dispatch/handler-deps.ts +1 -1
  256. package/src/transport/dispatch/leases.ts +1 -1
  257. package/src/transport/dispatch/retry-policy.ts +1 -1
  258. package/src/transport/dispatch/streaming/finalize.ts +2 -2
  259. package/src/transport/dispatch/streaming/stream.ts +1 -1
  260. package/src/transport/dispatch/upstream.ts +1 -1
  261. package/src/transport/shutdown-notice.ts +1 -1
  262. package/src/workers/buddy-growth-tasks.ts +8 -8
  263. package/src/workers/daily-checkin.ts +4 -4
  264. package/src/workers/quota-refresh-sweep.ts +1 -1
  265. package/src/workers/zcode-gift-claim.ts +1 -1
  266. package/dist/dashboard/assets/BannedPage-Cc0vKpMG.js.gz +0 -0
  267. package/dist/dashboard/assets/CliToolDetailPage-Bqj2aa1T.js.gz +0 -0
  268. package/dist/dashboard/assets/CliToolsPage-CXsaOAO2.js.gz +0 -0
  269. package/dist/dashboard/assets/CombosPage-BeLpJ4ma.js.gz +0 -0
  270. package/dist/dashboard/assets/ConsoleLogPage-DnV0uZ3m.js.gz +0 -0
  271. package/dist/dashboard/assets/CustomizationPage-CBxAjo63.js.gz +0 -0
  272. package/dist/dashboard/assets/HealthEventsModal-B5yEE0k6.js.gz +0 -0
  273. package/dist/dashboard/assets/InjectSystemPromptPanel-BWFLECmz.js.gz +0 -0
  274. package/dist/dashboard/assets/LoginPage-CgoPc5B9.js.gz +0 -0
  275. package/dist/dashboard/assets/ModelPicker-D9bFQern.js.gz +0 -0
  276. package/dist/dashboard/assets/OverviewPage-B_p4tL8k.js.gz +0 -0
  277. package/dist/dashboard/assets/ProviderDetailPage-rs5E2rxv.js +0 -8
  278. package/dist/dashboard/assets/ProviderDetailPage-rs5E2rxv.js.gz +0 -0
  279. package/dist/dashboard/assets/ProviderIcon-D_0PiZtg.js.gz +0 -0
  280. package/dist/dashboard/assets/ProvidersPage-CLI2bJTK.js.gz +0 -0
  281. package/dist/dashboard/assets/ProxyPage-BwNC_-Cx.js +0 -9
  282. package/dist/dashboard/assets/ProxyPage-BwNC_-Cx.js.gz +0 -0
  283. package/dist/dashboard/assets/QuotaPage-DA-a1iSN.js.gz +0 -0
  284. package/dist/dashboard/assets/SettingsPage-CctTga2C.js.gz +0 -0
  285. package/dist/dashboard/assets/SetupPage-febLU4r1.js.gz +0 -0
  286. package/dist/dashboard/assets/StudioPage-M6fvR5Wz.js.gz +0 -0
  287. package/dist/dashboard/assets/UsagePage-nyeuRqz9.js.gz +0 -0
  288. package/dist/dashboard/assets/api-keys-BFOlvrJC.js.gz +0 -0
  289. package/dist/dashboard/assets/app-Dx2QgsNj.css +0 -2
  290. package/dist/dashboard/assets/app-Dx2QgsNj.css.gz +0 -0
  291. package/dist/dashboard/assets/assert-CqTLw5e7.js.gz +0 -0
  292. package/dist/dashboard/assets/badge-C83EqLB0.js.gz +0 -0
  293. package/dist/dashboard/assets/banner-D1xn7pIe.js.gz +0 -0
  294. package/dist/dashboard/assets/card-DU3Lq5Lm.js.gz +0 -0
  295. package/dist/dashboard/assets/cli-tools-Dw1U_drK.js.gz +0 -0
  296. package/dist/dashboard/assets/clipboard-button-CEL5Sg_J.js.gz +0 -0
  297. package/dist/dashboard/assets/dialog-BjFR5UHy.js.gz +0 -0
  298. package/dist/dashboard/assets/drawer-HvBW0AjI.js.gz +0 -0
  299. package/dist/dashboard/assets/entry-BSu3gFlt.js.gz +0 -0
  300. package/dist/dashboard/assets/entry-CECVELyy.js.gz +0 -0
  301. package/dist/dashboard/assets/entry-DYYofQ7W.js.gz +0 -0
  302. package/dist/dashboard/assets/formatters-DLW4i8zK.js.gz +0 -0
  303. package/dist/dashboard/assets/index-1-fCqe64.js.gz +0 -0
  304. package/dist/dashboard/assets/inline-DGvU2oLK.js.gz +0 -0
  305. package/dist/dashboard/assets/input-iNnO1X2K.js.gz +0 -0
  306. package/dist/dashboard/assets/layout-0q8UGypb.js.gz +0 -0
  307. package/dist/dashboard/assets/list-row-C0w2bQjJ.js.gz +0 -0
  308. package/dist/dashboard/assets/live-M5aJ_xCa.js.gz +0 -0
  309. package/dist/dashboard/assets/network-BKeQGT3K.js.gz +0 -0
  310. package/dist/dashboard/assets/numbers-CivAk3da.js.gz +0 -0
  311. package/dist/dashboard/assets/pagination-CpzvI5ep.js.gz +0 -0
  312. package/dist/dashboard/assets/routing-BFQX2VBc.js.gz +0 -0
  313. package/dist/dashboard/assets/select-CXbck0jG.js.gz +0 -0
  314. package/dist/dashboard/assets/settings-C_XvMHh9.js.gz +0 -0
  315. package/dist/dashboard/assets/stack-DggRiCxE.js.gz +0 -0
  316. package/dist/dashboard/assets/state-ClFzfbYp.js.gz +0 -0
  317. package/dist/dashboard/assets/switch-CTIFxKqS.js.gz +0 -0
  318. package/dist/dashboard/assets/useMutation-BClG3yWj.js.gz +0 -0
  319. package/src/console/providers/catalog/discover-constants.ts +0 -6
  320. package/src/network/pool-health.ts +0 -198
  321. package/src/providers/integrations/buddy/buddy-checkin.ts +0 -521
  322. package/src/providers/integrations/buddy/buddy-cli-headers.ts +0 -76
  323. package/src/providers/integrations/buddy/buddy-conversation-headers.ts +0 -130
  324. package/src/providers/integrations/buddy/buddy-discovery-shared.ts +0 -234
  325. package/src/providers/integrations/buddy/buddy-growth-shared.ts +0 -496
  326. package/src/providers/integrations/buddy/buddy-oauth-shared.ts +0 -341
  327. package/src/providers/integrations/buddy/buddy-quota-shared.ts +0 -417
  328. package/src/providers/integrations/buddy/buddy-server-tools.ts +0 -240
  329. package/src/providers/integrations/buddy/buddy-usage-shared.ts +0 -221
  330. package/src/providers/integrations/buddy/buddy-usage-store.ts +0 -181
  331. package/src/providers/integrations/buddy/codebuddy/cn-catalog.ts +0 -37
  332. package/src/providers/integrations/buddy/codebuddy/cn-provider.ts +0 -188
  333. package/src/providers/integrations/buddy/codebuddy/oauth.ts +0 -47
  334. package/src/providers/integrations/buddy/codebuddy/provider.ts +0 -179
  335. package/src/providers/integrations/buddy/codebuddy/quota.ts +0 -154
  336. package/src/providers/integrations/qoder/chat-proto.ts +0 -328
  337. package/src/providers/integrations/qoder/qoder-grpc.ts +0 -870
  338. package/src/providers/integrations/qoder/qoder-ide.ts +0 -481
  339. /package/dist/dashboard/assets/{app-DIXLjojg.js → app-C0gTXEoN.js} +0 -0
  340. /package/dist/dashboard/assets/{app-DIXLjojg.js.gz → app-C0gTXEoN.js.gz} +0 -0
  341. /package/src/network/{http2-bidi.ts → bidi.ts} +0 -0
  342. /package/src/network/pool/{byte-accounting.ts → accounting.ts} +0 -0
  343. /package/src/network/pool/{pool-scripts.ts → scripts.ts} +0 -0
  344. /package/src/network/pool/{pool-state.ts → state.ts} +0 -0
  345. /package/src/network/{backoff.ts → util/backoff.ts} +0 -0
  346. /package/src/network/{deduplication.ts → util/dedupe.ts} +0 -0
  347. /package/src/network/{ip-utils.ts → util/ip.ts} +0 -0
  348. /package/src/network/{rate-limit-verdict.ts → verdict.ts} +0 -0
@@ -4,7 +4,7 @@ import type { ApiKeyProviderSpec } from "../../configured-provider";
4
4
  import { isRecord } from "../../../protocol/codecs";
5
5
  import { boundedUpstreamArray, boundedUpstreamNumber, sanitizeUpstreamLabel } from "../../provider-sanitize";
6
6
  import { getHermesVersion } from "../../operations/client-versions";
7
- import { clineReasoningReplayPrepares } from "../buddy/buddy-chat-shared";
7
+ import { clineReasoningReplayPrepares } from "../buddy/chat";
8
8
 
9
9
  const HERMES_PROVIDER_ID = "hermes" as const;
10
10
 
@@ -85,10 +85,16 @@ export const HERMES_MODELS: readonly ModelDefinition[] = [
85
85
  // entirely and dispatching it answers 404 "This model's free period has
86
86
  // ended" — a retired row, not a capacity blip, so it cannot be left in.
87
87
  //
88
- // `poolside/laguna-s-2.1:free`, `poolside/laguna-xs-2.1:free`, and
89
- // `stepfun/step-3.7-flash:free` are removed too: they fail at dispatch.
88
+ // `poolside/laguna-s-2.1:free` and `poolside/laguna-xs-2.1:free` are
89
+ // removed: they fail at dispatch.
90
+ //
91
+ // `stepfun/step-3.7-flash:free` is kept with a corrected output cap: the
92
+ // upstream serves a 256k context total (input + output), so the catalog's
93
+ // 256k `max_completion_tokens` makes every request exceed the endpoint and
94
+ // fail with "you requested about 259137 tokens". Clamped to 128k output the
95
+ // row fits the endpoint.
96
+ defineModel({ id: "stepfun/step-3.7-flash:free", ctx: 256_000, out: 128_000, reasoning: true, free: true, freeTier: true }),
90
97
  defineModel({ id: "upstage/solar-mini4:free", ctx: 524288, out: 131072, reasoning: true, free: true, freeTier: true }),
91
- defineModel({ id: "reflection/beam-1.0-501b-a23b:free", ctx: 262144, out: 131072, reasoning: true, free: true, freeTier: true }),
92
98
  // Vision-corrected against the live catalog: `architecture.input_modalities`
93
99
  // lists `image` for this row, so it was published without vision it has.
94
100
  defineModel({ id: "meituan/longcat-2.5:free", ctx: 1048576, out: 131072, vision: true, reasoning: true, free: true, freeTier: true }),
@@ -162,16 +168,39 @@ export async function discoverHermesFreeModels(options: {
162
168
  if (rows === undefined) return null;
163
169
 
164
170
  const byId = new Map<string, ModelDefinition>();
171
+ // Ids the live catalog still lists as free but that fail at dispatch (404,
172
+ // retired, or a context window the catalog mis-states) — never surfaced as
173
+ // cards again.
174
+ const blockedIds = new Set([
175
+ "tencent/hy3:free",
176
+ "upstage/solar-pro4:free",
177
+ "meituan/longcat-2.0:free",
178
+ "stepfun/step-5-preview:free",
179
+ "poolside/laguna-s-2.1:free",
180
+ "poolside/laguna-xs-2.1:free",
181
+ "reflection/beam-1.0-501b-a23b:free",
182
+ ]);
165
183
  for (const entry of rows) {
166
184
  if (!isRecord(entry)) continue;
167
185
  const id = sanitizeUpstreamLabel(entry["id"]);
168
186
  if (id === undefined) continue;
187
+ if (blockedIds.has(id)) continue;
169
188
  if (!isFreePriced(entry)) continue;
170
189
 
171
190
  const context = boundedUpstreamNumber(entry["context_length"], { min: 1, max: 1_000_000_000 }) ?? undefined;
172
191
  const topProvider = isRecord(entry["top_provider"]) ? entry["top_provider"] : undefined;
173
- const output =
192
+ const rawOutput =
174
193
  boundedUpstreamNumber(topProvider?.["max_completion_tokens"], { min: 1, max: 1_000_000_000 }) ?? undefined;
194
+ // The upstream's `max_completion_tokens` is a total budget on some rows
195
+ // (e.g. `stepfun/step-3.7-flash:free` rejects any request above its 256k
196
+ // context). The gateway's output cap is a per-request token allowance, so
197
+ // clamp it under the context window to keep requests admissible.
198
+ const output =
199
+ rawOutput === undefined
200
+ ? undefined
201
+ : context === undefined || rawOutput < context
202
+ ? rawOutput
203
+ : Math.max(1, Math.floor(context * 0.75));
175
204
  const architecture = isRecord(entry["architecture"]) ? entry["architecture"] : undefined;
176
205
  const inputs = boundedUpstreamArray(architecture?.["input_modalities"]) ?? [];
177
206
  const hasImage = inputs.includes("image");
@@ -109,8 +109,8 @@ export class KiloOAuthClient extends OAuthDeviceFlow {
109
109
  super(fetchFn);
110
110
  }
111
111
 
112
- override async startDeviceAuth(_context?: OAuthDeviceFlowContext): Promise<OAuthDeviceStartResult> {
113
- const response = await this.fetchFn(KILO_DEVICE_CODES_URL, {
112
+ override async startDeviceAuth(context?: OAuthDeviceFlowContext): Promise<OAuthDeviceStartResult> {
113
+ const response = await this.fetchFor(context)(KILO_DEVICE_CODES_URL, {
114
114
  method: "POST",
115
115
  headers: { "content-type": "application/json", accept: "application/json" },
116
116
  });
@@ -141,7 +141,7 @@ export class KiloOAuthClient extends OAuthDeviceFlow {
141
141
  context?: OAuthDeviceFlowContext,
142
142
  ): Promise<OAuthDevicePollResult> {
143
143
  const pollKey = context?.providerState ?? deviceAuthId;
144
- const response = await this.fetchFn(`${KILO_DEVICE_CODES_URL}/${encodeURIComponent(pollKey)}`, {
144
+ const response = await this.fetchFor(context)(`${KILO_DEVICE_CODES_URL}/${encodeURIComponent(pollKey)}`, {
145
145
  headers: { accept: "application/json" },
146
146
  });
147
147
  if (response.status === 202) return { status: "pending" };
@@ -165,7 +165,7 @@ export class KiloOAuthClient extends OAuthDeviceFlow {
165
165
  if (nonEmptyTrimmedString(payload.status) !== "approved" || accessToken === undefined) {
166
166
  return { status: "pending" };
167
167
  }
168
- const orgId = await fetchOrganizationId(this.fetchFn, accessToken, AbortSignal.timeout(10_000));
168
+ const orgId = await fetchOrganizationId(this.fetchFor(context), accessToken, AbortSignal.timeout(10_000));
169
169
  const label = accountLabelFrom(payload);
170
170
  const result: OAuthExchangeResult = {
171
171
  access: encodeKiloCredential(accessToken, orgId),
@@ -24,7 +24,7 @@ import type { CanonicalRequest } from "../../../protocol/canonical-model";
24
24
  import { OpenAICompatibleAdapter, withBearerAuthentication } from "../../adapters/compatible-adapter";
25
25
  import { providerBaseUrl } from "../../provider-metadata";
26
26
  import { decodeKiloCredential } from "../../../security/credential-codec";
27
- import { clineReasoningReplayPrepares } from "../buddy/buddy-chat-shared";
27
+ import { clineReasoningReplayPrepares } from "../buddy/chat";
28
28
 
29
29
  export const KILO_PROVIDER_ID = "kilo" as const;
30
30
  export const KILO_BASE_URL = providerBaseUrl("kilo");
@@ -123,13 +123,13 @@ export class KimiCodeOAuthClient extends OAuthDeviceFlow {
123
123
  super(fetchFn);
124
124
  }
125
125
 
126
- override async startDeviceAuth(_context?: OAuthDeviceFlowContext): Promise<OAuthDeviceStartResult & { providerState: string }> {
126
+ override async startDeviceAuth(context?: OAuthDeviceFlowContext): Promise<OAuthDeviceStartResult & { providerState: string }> {
127
127
  const deviceId = createKimiDeviceId();
128
128
  // The Kimi device-authorization endpoint only parses
129
129
  // `application/x-www-form-urlencoded` bodies — JSON `client_id` is
130
130
  // rejected with `client_id is required`.
131
131
  const body = new URLSearchParams({ client_id: KIMI_CODE_CLIENT_ID });
132
- const response = await this.fetchFn(KIMI_CODE_DEVICE_AUTHORIZE_URL, {
132
+ const response = await this.fetchFor(context)(KIMI_CODE_DEVICE_AUTHORIZE_URL, {
133
133
  method: "POST",
134
134
  headers: { ...jsonHeaders(deviceId), "content-type": "application/x-www-form-urlencoded" },
135
135
  body,
@@ -158,7 +158,7 @@ export class KimiCodeOAuthClient extends OAuthDeviceFlow {
158
158
  device_code: deviceAuthId,
159
159
  grant_type: "urn:ietf:params:oauth:grant-type:device_code",
160
160
  });
161
- const response = await this.fetchFn(KIMI_CODE_TOKEN_URL, {
161
+ const response = await this.fetchFor(context)(KIMI_CODE_TOKEN_URL, {
162
162
  method: "POST",
163
163
  headers: { ...jsonHeaders(deviceId), "content-type": "application/x-www-form-urlencoded" },
164
164
  body,
@@ -504,7 +504,7 @@ export class KiroOAuthClient extends OAuthDeviceFlow {
504
504
  const region = assertAwsRegion(registration.region);
505
505
  let response: Response;
506
506
  try {
507
- response = await this.fetchFn(`https://oidc.${region}.amazonaws.com/token`, {
507
+ response = await this.fetchFor(context)(`https://oidc.${region}.amazonaws.com/token`, {
508
508
  method: "POST",
509
509
  headers: ssoOidcHeaders(),
510
510
  body: JSON.stringify({
@@ -548,7 +548,7 @@ export class KiroOAuthClient extends OAuthDeviceFlow {
548
548
  // token is opaque, so ask the usage surface for the account email.
549
549
  if (label === kiroDefaultLabel(registration.authMethod)) {
550
550
  try {
551
- const quota = await fetchKiroQuota(access, this.fetchFn, { auth_state: authStateOf(authState) });
551
+ const quota = await fetchKiroQuota(access, this.fetchFor(context), { auth_state: authStateOf(authState) });
552
552
  if (quota.accountLabel !== undefined && quota.accountLabel.trim().length > 0) {
553
553
  label = quota.accountLabel;
554
554
  }
@@ -131,25 +131,25 @@ export class MuseCodeOAuthClient extends OAuthDeviceFlow {
131
131
  protected override readonly tokenUrl = MUSE_CODE_DEVICE_TOKEN_URL;
132
132
  protected override readonly scopes = "";
133
133
 
134
- override async startDeviceAuth(_context?: OAuthDeviceFlowContext): Promise<OAuthDeviceStartResult> {
134
+ override async startDeviceAuth(context?: OAuthDeviceFlowContext): Promise<OAuthDeviceStartResult> {
135
135
  return this.startGenericDeviceAuth({
136
136
  tokenUrl: MUSE_CODE_DEVICE_TOKEN_URL,
137
137
  deviceAuthUrl: MUSE_CODE_DEVICE_AUTHORIZE_URL,
138
138
  clientId: MUSE_CODE_CLIENT_ID,
139
139
  formHeaders: metaHeaders,
140
- });
140
+ }, context);
141
141
  }
142
142
 
143
143
  override async pollDeviceAuth(
144
144
  deviceAuthId: string,
145
- _context?: OAuthDeviceFlowContext,
145
+ context?: OAuthDeviceFlowContext,
146
146
  ): Promise<OAuthDevicePollResult> {
147
147
  const result = await this.pollGenericDeviceAuth(deviceAuthId, {
148
148
  tokenUrl: MUSE_CODE_DEVICE_TOKEN_URL,
149
149
  deviceAuthUrl: MUSE_CODE_DEVICE_AUTHORIZE_URL,
150
150
  clientId: MUSE_CODE_CLIENT_ID,
151
151
  formHeaders: metaHeaders,
152
- });
152
+ }, context);
153
153
  if (result.status !== "complete") return result;
154
154
  const accessToken = result.result.access;
155
155
  // The key mint runs after approval: a 429, a payment gate, or a transient
@@ -157,7 +157,7 @@ export class MuseCodeOAuthClient extends OAuthDeviceFlow {
157
157
  // exception that escapes the dashboard's poll request.
158
158
  let keyPayload;
159
159
  try {
160
- keyPayload = await requestMuseCodeKey(accessToken, this.fetchFn);
160
+ keyPayload = await requestMuseCodeKey(accessToken, this.fetchFor(context));
161
161
  } catch (error) {
162
162
  return { status: "failed", reason: error instanceof Error ? error.message : "Muse Code key exchange failed" };
163
163
  }
@@ -9,7 +9,7 @@ import { createHash, randomBytes } from "node:crypto";
9
9
  * can pin the exact identity sent before discovery resolves. Keep both in
10
10
  * lockstep when the CLI releases.
11
11
  */
12
- export const OPENCODE_CLIENT_VERSION = "2.2.6";
12
+ export const OPENCODE_CLIENT_VERSION = "2.2.7";
13
13
 
14
14
  let lastTimestamp = 0;
15
15
  let sequenceCounter = 0;
@@ -5,7 +5,7 @@ import { providerBaseUrl } from "../../provider-metadata";
5
5
  import type { DiscoveryInput } from "../../discovery/discovery-types";
6
6
  import { defineModel } from "../../provider-model-definition";
7
7
  import { openrouterFreeRoster } from "../../discovery/openrouter-free-roster";
8
- import { clineReasoningReplayPrepares } from "../buddy/buddy-chat-shared";
8
+ import { clineReasoningReplayPrepares } from "../buddy/chat";
9
9
 
10
10
  const OPENROUTER_PROVIDER_ID = "openrouter" as const;
11
11
 
@@ -27,9 +27,6 @@ import { PERCH_STARTER_PINS } from "./perch-realm";
27
27
  /** Default hosted model id when the account endpoint is unreachable. */
28
28
  export const PERCH_DEFAULT_CHAT_MODEL = "wandb-kimi-k2-6";
29
29
 
30
- /** The CLI user-agent the upstream recognizes as a CLI surface. */
31
- export const PERCH_CLI_USER_AGENT = "perchai-cli/unknown";
32
-
33
30
  export interface PerchTurnContext {
34
31
  readonly userId: string | null;
35
32
  readonly workspaceId: string | null;
@@ -27,8 +27,8 @@ import {
27
27
  type ProviderId,
28
28
  } from "../../provider-contracts";
29
29
  import { usageFromProvider } from "../../provider-usage";
30
+ import { getPerchVersion } from "../../operations/client-versions";
30
31
  import {
31
- PERCH_CLI_USER_AGENT,
32
32
  PERCH_SAFFRON_SYSTEM_PROMPT,
33
33
  buildPerchModelCallBody,
34
34
  parsePerchSse,
@@ -96,7 +96,7 @@ export class PerchAdapter implements ProviderAdapter {
96
96
  headers: {
97
97
  authorization: `Bearer ${access}`,
98
98
  "content-type": "application/json",
99
- "user-agent": PERCH_CLI_USER_AGENT,
99
+ "user-agent": `perchai-cli/${getPerchVersion()}`,
100
100
  "x-perch-locale": "en",
101
101
  },
102
102
  body: JSON.stringify({ surface: "cli", profile: "standard" }),
@@ -164,7 +164,7 @@ export class PerchAdapter implements ProviderAdapter {
164
164
  authorization: `Bearer ${access}`,
165
165
  "content-type": "application/json",
166
166
  accept: "text/event-stream",
167
- "user-agent": PERCH_CLI_USER_AGENT,
167
+ "user-agent": `perchai-cli/${getPerchVersion()}`,
168
168
  "x-perch-locale": "en",
169
169
  "x-perch-call-delta": "1",
170
170
  "x-perch-turn-ticket": ticket.ticket,
@@ -15,7 +15,7 @@
15
15
  * 401; verified live 2026-11-04).
16
16
  */
17
17
  import type { CartethyiaDatabase } from "../../../persistence/db-handle";
18
- import type { ValidatedFetch } from "../../../network/outbound-fetch";
18
+ import type { ValidatedFetch } from "../../../network/egress/fetch";
19
19
  import { qoderRealmForProvider, QODER_REALMS } from "./qoder-realm";
20
20
  import { exchangeQoderPatForHost } from "./qoder-auth";
21
21
  import {
@@ -1,21 +1,17 @@
1
1
  /**
2
2
  * Qoder adapter — transport selection, credential resolution, dispatch.
3
3
  *
4
- * Surface contract (2026-11-07):
4
+ * Surface contract (2026-11-07, verified live 2026-11-11):
5
5
  * - `cosy` (default, production): `/algo/.../agent_chat_generation` on
6
- * `api1` → `api2` → `api3.qoder.sh` (legacy COSY) — the only surface
7
- * verified alive. Accepts free-tier rows (`lite`).
8
- * - `grpc` (`model.chat.ChatService/ChatCompletionStream`): **not the
9
- * default anymore** — its host (`api2-v2.qoder.sh`) is NXDOMAIN since
10
- * 2026-11-07. Kept as an explicit override for probes.
11
- * - `ide` (`/model/v1/chat/completions`): **probe/testing only — never
12
- * production.** It is the only surface that echoes strategy metadata
13
- * (`raw_usage` → provider_model/target) and it streams immediately, but its
14
- * ~256 KiB body cap cannot carry the 1M window the roster declares. Dispatch
15
- * reaches it only when `probe_surface: "ide"` is set (discovery probes) or
16
- * the option `surface: "ide"` is forced explicitly for local testing.
6
+ * the realm's token-kind-specific gateway set (legacy COSY) — the only
7
+ * surface verified alive. A PAT is exchanged to a job token first; job
8
+ * tokens use `api2` → `api1` only, since `api3` rejects them with
9
+ * `105 Login expired` (403). Accepts free-tier rows (`lite`).
10
+ * - The gRPC surface (`model.chat.ChatService/ChatCompletionStream`) and the
11
+ * IDE surface (`/model/v1/chat/completions`) are dead on every host that
12
+ * resolves (gRPC: UNIMPLEMENTED; IDE: 404) since 2026-11-07 and are not
13
+ * dispatched anymore.
17
14
  */
18
- import { randomUUID } from "node:crypto";
19
15
  import { GatewayError } from "../../../protocol/gateway-error";
20
16
  import type { CanonicalEvent, CanonicalRequest } from "../../../protocol/canonical-model";
21
17
  import {
@@ -28,19 +24,16 @@ import {
28
24
  } from "../../provider-contracts";
29
25
  import { abortGatewayError, createUpstreamDeadlineLifecycle } from "../../operations/upstream-deadline";
30
26
  import { getQoderVersion } from "../../operations/client-versions";
31
- import { qoderMachineHostname, qoderMachineOs } from "../../operations/cli-platform";
32
27
  import { mapUpstreamHttpError } from "../../../transport/failure-policy";
33
28
  import { QODER_CN_MODEL_CONFIGS, QODER_INTL_MODEL_CONFIGS } from "./qoder-catalog";
34
29
  import { isQoderVirtualModel, qoderDispatchModelId } from "./qoder-virtual";
35
30
  import { isQoderPat, qoderOAuthAuth, qoderSessionId, resolveQoderAuth } from "./qoder-auth";
36
31
  import { buildQoderRequest, callQoder } from "./qoder-chat";
37
- import { buildQoderGrpcRequest, qoderGrpcMetadata, qoderGrpcToCanonicalEvents } from "./qoder-grpc";
38
- import { buildQoderIdeRequest, qoderBodyToCanonicalEvents } from "./qoder-ide";
32
+ import { qoderBodyToCanonicalEvents } from "./qoder-sse";
39
33
  import { estimateQoderSessionCache } from "./qoder-cache";
40
34
  import {
41
35
  QODER_CHAT_PATH,
42
36
  QODER_PROVIDER_ID,
43
- qoderIdeChatUrl,
44
37
  qoderInferenceCandidates,
45
38
  type QoderRealm,
46
39
  } from "./qoder-realm";
@@ -56,38 +49,23 @@ interface QoderAdapterOptions {
56
49
  * Which chat transport to dispatch on.
57
50
  *
58
51
  * `cosy` (default) = legacy COSY (`/algo/.../agent_chat_generation`) on
59
- * `api1` → `api2` → `api3.qoder.sh` — the only surface verified alive
60
- * (2026-11-07; the `api2-v2` model-server host is NXDOMAIN). This path also
61
- * accepts free-tier rows (`lite`).
62
- *
63
- * `grpc` forces the gRPC surface (`model.chat.ChatService/ChatCompletionStream`)
64
- * — **no longer the default**; its host is not verified alive.
65
- *
66
- * `ide` forces `/model/v1` — **probe/testing only, never production**
67
- * (see module doc).
52
+ * the realm's gateway candidates (device tokens: `api1` → `api2` → `api3`;
53
+ * job tokens minted from a PAT: `api2` → `api1` only — `api3` answers
54
+ * `105 Login expired`). The `api2-v2` model-server host is NXDOMAIN, and
55
+ * the gRPC service is UNIMPLEMENTED on every host that resolves. This path
56
+ * also accepts free-tier rows (`lite`).
68
57
  */
69
- readonly surface?: "grpc" | "cosy" | "ide";
58
+ readonly surface?: "cosy";
70
59
  }
71
60
 
72
61
  /** Resolves the transport for one dispatch. Exported for tests. */
73
- export function resolveQoderTransport(
74
- surface: "grpc" | "cosy" | "ide" | undefined,
75
- modelConfig: QoderModelConfig,
76
- ): "grpc" | "cosy" | "ide" {
77
- const chosen = surface ?? "cosy";
78
- // `ide` is an explicit testing override — never auto-selected, and it does
79
- // not apply the free-tier rule (a forced IDE call is a test call).
80
- if (chosen === "ide") return "ide";
81
- // Free-tier rows (`lite`) are refused on gRPC with `quota exceeded` (116);
82
- // they must use the legacy COSY surface.
83
- if (chosen === "grpc" && modelConfig.is_free === true) return "cosy";
84
- return chosen;
62
+ export function resolveQoderTransport(): "cosy" {
63
+ return "cosy";
85
64
  }
86
65
 
87
66
  class QoderAdapter implements ProviderAdapter {
88
67
  readonly provider_id: ProviderId;
89
68
  private readonly realm: QoderRealm;
90
- private readonly surface: "grpc" | "cosy" | "ide";
91
69
  private readonly fetchFn: typeof fetch;
92
70
 
93
71
  constructor(options: QoderAdapterOptions = {}) {
@@ -100,7 +78,6 @@ class QoderAdapter implements ProviderAdapter {
100
78
  // mismatch that made `deepseek-v4.1-flash` fail while `qwen3.8-flash`
101
79
  // passed. This is the same default `resolveQoderTransport(undefined)`
102
80
  // already returns.
103
- this.surface = options.surface ?? "cosy";
104
81
  this.fetchFn = options.fetch ?? globalThis.fetch;
105
82
  }
106
83
 
@@ -180,7 +157,7 @@ class QoderAdapter implements ProviderAdapter {
180
157
 
181
158
  const lifecycle = createUpstreamDeadlineLifecycle(context);
182
159
 
183
- // Outbound fetch respects SSRF binding when provided
160
+ // Outbound fetch respects SSRF binding when provided.
184
161
  const outboundFetch: typeof fetch = (context.outbound_fetch as unknown as typeof fetch) ?? this.fetchFn;
185
162
 
186
163
  const fetcher: (url: string, init: RequestInit) => Promise<Response> = (url, init) =>
@@ -196,77 +173,6 @@ class QoderAdapter implements ProviderAdapter {
196
173
  : qoderOAuthAuth(secret, context.credential.auth_state, context.credential.account_id);
197
174
 
198
175
  // Transport selection — see module doc for the surface contract.
199
- const forceIde = this.surface === "ide" || candidate.probe_surface === "ide";
200
- const useGrpc = !forceIde && resolveQoderTransport(this.surface, modelConfig) === "grpc";
201
-
202
- if (forceIde) {
203
- const ideUrl = qoderIdeChatUrl(this.realm);
204
- const ideBody = buildQoderIdeRequest(modelId, request, modelConfig, auth, this.realm, version);
205
- // Header fingerprint mirrors the desktop client's IDE surface (`PFn` /
206
- // `Hc`): X-Request-ID normalized to uppercase, Cosy-Version, real
207
- // machine OS, and the sanitized hostname on infer SSE requests. The
208
- // User-Agent is the real IDE client's (`Bun/1.4.4`, live capture) and
209
- // `X-Session-ID` matches the body's session id (sha256 userId:model).
210
- const ideHostname = qoderMachineHostname();
211
- const res = await fetcher(ideUrl, {
212
- method: "POST",
213
- headers: {
214
- Authorization: `Bearer ${auth.securityOauthToken}`,
215
- "Content-Type": "application/json",
216
- Accept: "text/event-stream",
217
- "User-Agent": "Bun/1.4.4",
218
- "X-Request-ID": randomUUID().toUpperCase(),
219
- "X-Session-ID": qoderSessionId(auth.userId, modelId),
220
- "Cosy-Version": version,
221
- "Cosy-ClientType": "5",
222
- "Cosy-MachineOS": qoderMachineOs(),
223
- ...(ideHostname.length > 0 ? { "Cosy-MachineHostname": ideHostname } : {}),
224
- },
225
- body: JSON.stringify(ideBody),
226
- });
227
- if (!res.ok) throw await mapUpstreamHttpError(res, "qoder");
228
- if (!res.body) throw new GatewayError("platform_unavailable", 502, "Qoder returned empty body (request {requestId}). The upstream came back empty-handed — retry 🌙", {}, "upstream");
229
- lifecycle.release();
230
- yield* this.#withSessionCacheEstimate(
231
- qoderBodyToCanonicalEvents(res.body as ReadableStream<Uint8Array>, lifecycle.signal, modelId),
232
- request,
233
- qoderSessionId(auth.userId, modelId),
234
- );
235
- return;
236
- }
237
-
238
- if (useGrpc) {
239
- // The gRPC request mirrors the HTTP surfaces' OpenAI-shaped body, so
240
- // the shared builder supplies the messages, tool declarations, and
241
- // tool-choice knobs both surfaces expect. Without the `tools` array
242
- // the upstream sees no toolset and answers with a DSML text block (or
243
- // claims no tool exists) instead of a structured `tool_calls`.
244
- const legacy = buildQoderRequest(modelId, request, modelConfig, auth, this.realm);
245
- const grpcRequest = buildQoderGrpcRequest(
246
- modelId,
247
- request,
248
- auth.userId,
249
- {
250
- messages: legacy.messages as Array<Record<string, unknown>>,
251
- tools: legacy.tools as Array<Record<string, unknown>>,
252
- ...(legacy.parameters === undefined
253
- ? {}
254
- : { parameters: legacy.parameters as Record<string, unknown> }),
255
- },
256
- );
257
- const grpcMetadata = qoderGrpcMetadata(auth);
258
- lifecycle.release();
259
- yield* this.#withSessionCacheEstimate(
260
- qoderGrpcToCanonicalEvents(grpcRequest, grpcMetadata, this.realm, lifecycle.signal, context.provider_capture),
261
- request,
262
- qoderSessionId(auth.userId, modelId),
263
- );
264
- return;
265
- }
266
-
267
- // Legacy COSY hosts are tried in order (api1 → api2 → api3) with the
268
- // endpoint path from discovery when present. A network-level failure on
269
- // one host falls through to the next; an HTTP error response surfaces as-is.
270
176
  const cosyCandidates = candidate.endpoint_path?.startsWith("http")
271
177
  ? [candidate.endpoint_path]
272
178
  : qoderInferenceCandidates(this.realm, auth.securityOauthToken).map(
@@ -71,6 +71,7 @@ export function qoderOAuthAuth(
71
71
  securityOauthToken: secret,
72
72
  refreshToken: "",
73
73
  machineId: state.machineId || (accountId ? qoderMachineIdForAccount(accountId) : randomUUID()),
74
+ ...(state.httpDnsIp !== undefined ? { httpDnsIp: state.httpDnsIp } : {}),
74
75
  };
75
76
  }
76
77
 
@@ -263,7 +264,13 @@ export async function resolveQoderAuth(input: {
263
264
  }): Promise<QoderAuth> {
264
265
  const realm = input.realm ?? "intl";
265
266
  if (isQoderPat(input.secret)) {
266
- return await exchangeQoderPat(input.secret, input.signal, input.fetcher, getQoderVersion(), realm);
267
+ const auth = await exchangeQoderPat(input.secret, input.signal, input.fetcher, getQoderVersion(), realm);
268
+ // A PAT exchange never returns the account's identity; when the persisted
269
+ // auth_state also carries the HTTPDNS-route IP, carry it over so the COSY
270
+ // request mirrors the desktop client's `X-Qoder-HTTPDNS-IP` header.
271
+ const state = parseQoderOAuthState(input.authState);
272
+ if (state?.httpDnsIp !== undefined) auth.httpDnsIp = state.httpDnsIp;
273
+ return auth;
267
274
  }
268
275
  // Discovery is handed only the secret and the persisted state, so the
269
276
  // account id is read from that state — the same place `qoderOAuthAuth` reads
@@ -390,7 +390,7 @@ export type QoderSigner = (url: string, body: string) => Promise<Record<string,
390
390
  * `fetcher` is optional in the type but required at runtime, and there is
391
391
  * deliberately no global-`fetch` default: a test cannot reach the network by
392
392
  * accident, and production is forced through the SSRF-validated egress
393
- * (`network/outbound-fetch.ts`). That egress is a `node:http` path, which is
393
+ * (`network/egress/fetch.ts`). That egress is a `node:http` path, which is
394
394
  * also the only way a body reaches the wire on a GET — `fetch` itself rejects
395
395
  * `GET` + `body` with "request with GET/HEAD method cannot have body".
396
396
  */
@@ -4,8 +4,9 @@
4
4
  * `buildQoderRequest` translates one canonical request into the legacy
5
5
  * `/algo/.../agent_chat_generation` body (and, by reuse, the messages/tools
6
6
  * shared by the IDE and gRPC surfaces). `callQoder` signs and sends it over
7
- * the COSY surface with the real client's headers (Bun UA, machine identity,
8
- * `x-model-key/source`, SSE accept).
7
+ * the COSY surface with the app/worker's headers (undici UA, machine
8
+ * identity, business/scene app profile, `x-model-key/source`, SSE accept)
9
+ * and a zstd-compressed body (capture 2026-11-11).
9
10
  */
10
11
  import { createHash, randomUUID } from "node:crypto";
11
12
  import type {
@@ -18,7 +19,7 @@ import { reasoningTextForReplay } from "../../../protocol/reasoning-text";
18
19
  import { resolveImageSource } from "../../../protocol/codecs";
19
20
  import { getQoderVersion } from "../../operations/client-versions";
20
21
  import { qoderMaxContextFor, qoderRosterMaxOutput } from "./qoder-catalog";
21
- import { transformQoderBody, qoderBuildCosyHeaders } from "./qoder-cosy";
22
+ import { transformQoderBody, qoderBuildCosyHeaders, type QoderCosyProfile } from "./qoder-cosy";
22
23
  import { qoderSessionId } from "./qoder-auth";
23
24
  import { flattenCanonicalContent, qoderToolChoice, qoderTools } from "./qoder-body";
24
25
  import type { QoderRealm } from "./qoder-realm";
@@ -469,8 +470,11 @@ type QoderFetcher = (url: string, init: RequestInit) => Promise<Response>;
469
470
 
470
471
  /**
471
472
  * Signs and sends one legacy COSY chat request. The body is alphabet-scrambled
472
- * before signing; headers come from `qoderBuildCosyHeaders` plus the modern
473
- * `x-model-key`/`x-model-source` and SSE accepts.
473
+ * then zstd-compressed (as the desktop worker does on the wire, capture
474
+ * 2026-11-11) before signing; headers come from `qoderBuildCosyHeaders` plus
475
+ * the modern `x-model-key`/`x-model-source` and SSE accepts. When the runtime
476
+ * lacks zstd the scrambled body is sent uncompressed, matching the older CLI
477
+ * wire.
474
478
  */
475
479
  export async function callQoder(
476
480
  url: string,
@@ -480,21 +484,34 @@ export async function callQoder(
480
484
  signal: AbortSignal,
481
485
  fetcher: QoderFetcher,
482
486
  version = getQoderVersion(),
487
+ profile: QoderCosyProfile = "app",
483
488
  ): Promise<Response> {
484
489
  const encoded = transformQoderBody(JSON.stringify(body));
485
490
  const modelConfig = (body["model_config"] as Record<string, unknown> | null) ?? null;
491
+ // The desktop worker compresses the scrambled body with zstd and stamps
492
+ // `Content-Encoding: zstd` (capture 2026-11-11), but the COSY signature is
493
+ // verified over the *scrambled* body — the server decompresses before
494
+ // verifying (proven live 2026-11-11: signing over the zstd bytes returns
495
+ // `403 Signature invalid`). The CLI sends the plain scrambled body. Fall
496
+ // back to the plain scrambled body (no encoding header) when the runtime
497
+ // lacks zstd.
498
+ const compressed =
499
+ profile === "app" && typeof Bun?.zstdCompressSync === "function"
500
+ ? (Bun.zstdCompressSync(Buffer.from(encoded)) as Uint8Array<ArrayBuffer>)
501
+ : undefined;
502
+ const wireBody = (compressed ?? encoded) as unknown as Uint8Array;
486
503
  return fetcher(url, {
487
504
  method: "POST",
488
505
  headers: {
489
- ...qoderBuildCosyHeaders(encoded, url, auth, version),
506
+ ...qoderBuildCosyHeaders(encoded, url, auth, version, profile),
490
507
  ...(MODERN_PROFILE.sendModelSourceHeaders
491
508
  ? { "x-model-key": modelId, "x-model-source": typeof modelConfig?.["source"] === "string" ? (modelConfig["source"] as string) : "system" }
492
509
  : {}),
493
510
  Accept: "text/event-stream",
494
511
  "Cache-Control": "no-cache",
495
- "Accept-Encoding": "identity",
512
+ ...(compressed === undefined ? {} : { "Content-Encoding": "zstd" }),
496
513
  },
497
- body: encoded as unknown as BodyInit,
514
+ body: wireBody as unknown as BodyInit,
498
515
  signal,
499
516
  });
500
517
  }
@@ -32,7 +32,7 @@ import { log } from "../../../observability/logger";
32
32
 
33
33
  /** Structural fetch bound; Bun's own fetch type carries extra host props. */
34
34
  type Fetcher = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
35
- import type { DailyCheckinResult, DailyCheckinState } from "../buddy/buddy-checkin";
35
+ import type { DailyCheckinResult, DailyCheckinState } from "../buddy/lifecycle";
36
36
 
37
37
  export const QODER_CHECKIN_PROVIDER_IDS: readonly string[] = ["qoder", "qodercn"];
38
38