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
@@ -7,6 +7,7 @@
7
7
  * interpreted. Calls are returned only when their names were declared on the
8
8
  * request; an undeclared name is never promoted into executable tool input.
9
9
  */
10
+ import { MarkupStreamParser } from "../markup-stream-parser";
10
11
  export interface OmpTextToolCall {
11
12
  readonly id: string;
12
13
  readonly name: string;
@@ -29,77 +30,49 @@ const TOOL_NAME_PATTERN = /^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,63}$/;
29
30
  * The parser holds a possible envelope across SSE deltas, so a split opening
30
31
  * tag, argument tag, or closing tag is never emitted as visible prose. At
31
32
  * end-of-stream an unterminated envelope is dropped, not executed or leaked.
33
+ *
34
+ * The stateful prefix-buffer loop is the shared {@link MarkupStreamParser}
35
+ * scaffold; this class supplies the OMP grammar as a thin configuration.
32
36
  */
33
37
  export class OmpToolCallMarkupParser {
34
- readonly #allowedNames: ReadonlySet<string>;
35
- #pending = "";
36
- #callSequence = 0;
38
+ readonly #parser: MarkupStreamParser<OmpTextToolCall>;
37
39
 
38
40
  constructor(allowedNames: ReadonlySet<string>) {
39
- this.#allowedNames = allowedNames;
41
+ this.#parser = new MarkupStreamParser<OmpTextToolCall>({
42
+ openers: [OPEN_PREFIX],
43
+ classifyOpener: (pending, openIndex) => {
44
+ const remainder = pending.slice(openIndex);
45
+ const opening = OPEN_PATTERN.exec(remainder);
46
+ if (opening === null || opening[1] === undefined) {
47
+ if (couldBeOpening(remainder)) return { kind: "hold" };
48
+ return { kind: "literal" };
49
+ }
50
+ const marker = opening[1];
51
+ const closeTag = `</tool_calls:${marker}>`;
52
+ return {
53
+ kind: "found",
54
+ closer: closeTag,
55
+ parse: (block) => {
56
+ const parsed = parseEnvelope(block, marker);
57
+ if (parsed === undefined) return undefined;
58
+ if (parsed.some((call) => !allowedNames.has(call.name))) {
59
+ // A syntactically valid call to an undeclared tool is model
60
+ // text, not authority to invoke a tool the client did not offer.
61
+ return undefined;
62
+ }
63
+ return parsed;
64
+ },
65
+ callId: (call, _index, seq) => ({ ...call, id: `call_omp_${marker}_${seq}` }),
66
+ };
67
+ },
68
+ flush: (pending) =>
69
+ couldBeOpening(pending) || OPEN_PATTERN.test(pending) ? "" : pending,
70
+ });
40
71
  }
41
72
 
42
73
  /** Feeds one content or reasoning delta and returns only settled text. */
43
74
  push(chunk: string): OmpTextToolCallResult {
44
- this.#pending += chunk;
45
- let cursor = 0;
46
- let text = "";
47
- const calls: OmpTextToolCall[] = [];
48
-
49
- for (;;) {
50
- const openIndex = this.#pending.indexOf(OPEN_PREFIX, cursor);
51
- if (openIndex < 0) {
52
- const safeEnd = this.#settledEnd(cursor);
53
- text += this.#pending.slice(cursor, safeEnd);
54
- cursor = safeEnd;
55
- break;
56
- }
57
-
58
- text += this.#pending.slice(cursor, openIndex);
59
- const remainder = this.#pending.slice(openIndex);
60
- const opening = OPEN_PATTERN.exec(remainder);
61
- if (opening === null || opening[1] === undefined) {
62
- if (this.#couldBeOpening(remainder)) {
63
- cursor = openIndex;
64
- break;
65
- }
66
- text += "<";
67
- cursor = openIndex + 1;
68
- continue;
69
- }
70
-
71
- const marker = opening[1];
72
- const openTag = opening[0];
73
- const closeTag = `</tool_calls:${marker}>`;
74
- const closeIndex = this.#pending.indexOf(closeTag, openIndex + openTag.length);
75
- if (closeIndex < 0) {
76
- cursor = openIndex;
77
- break;
78
- }
79
-
80
- const envelopeEnd = closeIndex + closeTag.length;
81
- const block = this.#pending.slice(openIndex, envelopeEnd);
82
- const parsed = parseEnvelope(block, marker);
83
- if (parsed === undefined) {
84
- text += block;
85
- } else if (parsed.some((call) => !this.#allowedNames.has(call.name))) {
86
- // A syntactically valid call to an undeclared tool is model text, not
87
- // authority to invoke a tool the client did not offer.
88
- text += block;
89
- } else {
90
- for (const call of parsed) {
91
- calls.push({
92
- id: `call_omp_${marker}_${this.#callSequence++}`,
93
- name: call.name,
94
- arguments: call.arguments,
95
- });
96
- }
97
- }
98
- cursor = envelopeEnd;
99
- }
100
-
101
- this.#pending = this.#pending.slice(cursor);
102
- return { text, calls };
75
+ return this.#parser.push(chunk);
103
76
  }
104
77
 
105
78
  /**
@@ -107,31 +80,15 @@ export class OmpToolCallMarkupParser {
107
80
  * an unfinished envelope is deliberately discarded rather than leaked.
108
81
  */
109
82
  flush(): OmpTextToolCallResult {
110
- const pending = this.#pending;
111
- this.#pending = "";
112
- if (this.#couldBeOpening(pending) || OPEN_PATTERN.test(pending)) {
113
- return { text: "", calls: [] };
114
- }
115
- return { text: pending, calls: [] };
116
- }
117
-
118
- #settledEnd(start: number): number {
119
- const remaining = this.#pending.slice(start);
120
- const maxPrefixLength = Math.min(OPEN_PREFIX.length - 1, remaining.length);
121
- for (let length = maxPrefixLength; length > 0; length -= 1) {
122
- if (OPEN_PREFIX.startsWith(remaining.slice(-length))) {
123
- return this.#pending.length - length;
124
- }
125
- }
126
- return this.#pending.length;
83
+ return this.#parser.flush();
127
84
  }
85
+ }
128
86
 
129
- #couldBeOpening(value: string): boolean {
130
- if (value.length < OPEN_PREFIX.length) return OPEN_PREFIX.startsWith(value);
131
- if (!value.startsWith(OPEN_PREFIX)) return false;
132
- const suffix = value.slice(OPEN_PREFIX.length);
133
- return /^[0-9a-f]{0,8}$/.test(suffix) || /^[0-9a-f]{8}$/.test(suffix) && !value.endsWith(">");
134
- }
87
+ function couldBeOpening(value: string): boolean {
88
+ if (value.length < OPEN_PREFIX.length) return OPEN_PREFIX.startsWith(value);
89
+ if (!value.startsWith(OPEN_PREFIX)) return false;
90
+ const suffix = value.slice(OPEN_PREFIX.length);
91
+ return /^[0-9a-f]{0,8}$/.test(suffix) || /^[0-9a-f]{8}$/.test(suffix) && !value.endsWith(">");
135
92
  }
136
93
 
137
94
  /**
@@ -274,7 +274,16 @@ export function parseResponsesResponseToEvents(
274
274
  canonicalTerminal({
275
275
  sequenceNumber: events.length + 1,
276
276
  responseId: id,
277
- state: status === "cancelled" ? "aborted" : status === "failed" ? "failed" : "complete",
277
+ // `incomplete` maps to `aborted` (not `complete`): the surface encoder
278
+ // turns `aborted` into `response.incomplete`, while `complete` would
279
+ // emit `response.completed` for a response the upstream declared
280
+ // unfinished (max output tokens / content filter).
281
+ state:
282
+ status === "cancelled" || status === "incomplete"
283
+ ? "aborted"
284
+ : status === "failed"
285
+ ? "failed"
286
+ : "complete",
278
287
  stopReason: respJsonStopReason,
279
288
  providerStopReason: respJsonProviderStopReason,
280
289
  usage: usageFromProvider(usage),
@@ -740,8 +749,15 @@ export async function* decodeResponsesSseStream(
740
749
  const truncated = status === undefined;
741
750
  yield canonicalTerminal({
742
751
  sequenceNumber: seq++,
752
+ // `incomplete` maps to `aborted` (not `complete`): the surface encoder
753
+ // turns `aborted` into `response.incomplete`, while `complete` would
754
+ // emit `response.completed` for a stream the upstream declared unfinished.
743
755
  state:
744
- status === "cancelled" ? "aborted" : status === "failed" || truncated ? "failed" : "complete",
756
+ status === "cancelled" || status === "incomplete"
757
+ ? "aborted"
758
+ : status === "failed" || truncated
759
+ ? "failed"
760
+ : "complete",
745
761
  stopReason: responsesStopReason,
746
762
  providerStopReason: responsesProviderStopReason,
747
763
  usage: usageFromProvider(rawUsage),
@@ -1,4 +1,4 @@
1
- import type { CanonicalStopReason } from "./canonical-model";
1
+ import type { CanonicalStopReason, CanonicalTerminalEvent } from "./canonical-model";
2
2
 
3
3
  /**
4
4
  * Canonical stop reason mapped onto each surface's wire vocabulary.
@@ -56,6 +56,28 @@ export function chatFinishReason(reason: CanonicalStopReason | undefined): strin
56
56
  return canonicalStopReasonToWire("chat", reason, "stop");
57
57
  }
58
58
 
59
+ /**
60
+ * Completion's finish_reason, or `null` when the terminal recorded none.
61
+ *
62
+ * Completion inherits chat's wire vocabulary (`length`, `content_filter`,
63
+ * `stop`) but keeps its own convention: a terminal that is not complete
64
+ * (failed/aborted) already surfaced its failure elsewhere, so it yields
65
+ * `null` rather than a stop word, and a complete terminal flattens every
66
+ * reason other than `length`/`content_filter` to `stop`. The sync adapter and
67
+ * the streaming encoder both derive through this function so the two halves
68
+ * of the completion surface cannot drift.
69
+ */
70
+ export function completionFinishReason(
71
+ reason: CanonicalStopReason | undefined,
72
+ state: CanonicalTerminalEvent["state"],
73
+ ): string | null {
74
+ if (state !== "complete") return null;
75
+ if (reason === "length" || reason === "content_filter") {
76
+ return canonicalStopReasonToWire("chat", reason, "stop");
77
+ }
78
+ return "stop";
79
+ }
80
+
59
81
  /** Messages' stop_reason, or `null` when the terminal recorded none. */
60
82
  export function messagesStopReason(reason: CanonicalStopReason | undefined): string | null {
61
83
  return reason === undefined ? null : canonicalStopReasonToWire("messages", reason, "end_turn");
@@ -152,6 +152,118 @@ function pairedClose(
152
152
  return undefined;
153
153
  }
154
154
 
155
+ /**
156
+ * Stateful strip of paired thinking tags that preserves source order.
157
+ *
158
+ * The reasoning channel (e.g. Chat `reasoning_content`) is one stream where
159
+ * every byte — tagged or not — is thought. The two-bucket split used by
160
+ * `ThinkSplitter` (`{ text, reasoning }`) cannot express interleaving, so a
161
+ * call site that joins `${reasoning}${text}` reorders the stream: untagged
162
+ * prose that preceded a `<think>` span lands after it. This stripper instead
163
+ * emits the whole settled content with paired tags removed, in source order,
164
+ * while keeping `ThinkSplitter`'s stateful guarantees: a tag split across two
165
+ * deltas is recognized, code spans are left alone, and an unclosed span fails
166
+ * open at the cap.
167
+ */
168
+ export class ThinkingTagStripper {
169
+ #buffer = "";
170
+ #codeTicks = 0;
171
+ #codeFenced = false;
172
+ /** True once an opening tag has been seen and its close has not arrived yet. */
173
+ #inUnclosedSpan = false;
174
+ /** The opening tag currently held, restored verbatim on fail-open/flush. */
175
+ #openMarker = "";
176
+
177
+ /** Feeds a chunk; returns settled content with paired tags removed, in order. */
178
+ push(chunk: string): string {
179
+ this.#buffer += stripNul(chunk);
180
+ let out = "";
181
+ for (;;) {
182
+ if (this.#inUnclosedSpan) {
183
+ const closed = this.#findTag(CLOSE_TAGS);
184
+ if (closed === undefined) break;
185
+ out += this.#buffer.slice(0, closed.index);
186
+ this.#buffer = this.#buffer.slice(closed.index + closed.length);
187
+ this.#codeTicks = closed.codeTicks;
188
+ this.#codeFenced = closed.codeFenced;
189
+ this.#inUnclosedSpan = false;
190
+ this.#openMarker = "";
191
+ continue;
192
+ }
193
+ const opened = this.#findTag(OPEN_TAGS);
194
+ if (opened === undefined) break;
195
+ out += this.#buffer.slice(0, opened.index);
196
+ this.#openMarker = this.#buffer.slice(opened.index, opened.index + opened.length);
197
+ this.#buffer = this.#buffer.slice(opened.index + opened.length);
198
+ this.#codeTicks = opened.codeTicks;
199
+ this.#codeFenced = opened.codeFenced;
200
+ this.#inUnclosedSpan = true;
201
+ }
202
+ if (this.#inUnclosedSpan) {
203
+ // No close yet: hold the whole span content — including any text after it
204
+ // — until a later close or `flush()`. Streaming it now would release the
205
+ // span content before its opening tag on fail-open.
206
+ if (this.#buffer.length > MAX_THINK_BLOCK_BYTES) {
207
+ out += this.#openMarker + this.#buffer;
208
+ this.#buffer = "";
209
+ this.#inUnclosedSpan = false;
210
+ this.#openMarker = "";
211
+ }
212
+ return out;
213
+ }
214
+ const held = danglingTagPrefix(this.#buffer);
215
+ const settled = held.length > 0 ? this.#buffer.slice(0, -held.length) : this.#buffer;
216
+ this.#buffer = held;
217
+ return out + settled;
218
+ }
219
+
220
+ /** Releases content held back for a tag that never completed. */
221
+ flush(): string {
222
+ const rest = this.#buffer;
223
+ this.#buffer = "";
224
+ this.#codeTicks = 0;
225
+ this.#codeFenced = false;
226
+ if (this.#inUnclosedSpan) {
227
+ // An open tag that never closed is not evidence of reasoning — restore it
228
+ // byte-for-byte and release the whole tail as-is.
229
+ const marker = this.#openMarker;
230
+ this.#inUnclosedSpan = false;
231
+ this.#openMarker = "";
232
+ return marker + rest;
233
+ }
234
+ return rest;
235
+ }
236
+
237
+ #findTag(tags: readonly string[]): TagMatch | undefined {
238
+ const buffer = this.#buffer;
239
+ let ticks = this.#codeTicks;
240
+ let fenced = this.#codeFenced;
241
+ for (let i = 0; i < buffer.length; i++) {
242
+ const char = buffer[i];
243
+ if (char === "`") {
244
+ let run = 1;
245
+ while (buffer[i + run] === "`") run++;
246
+ if (ticks === 0) {
247
+ ticks = run;
248
+ fenced = run >= 3;
249
+ } else if (run === ticks) {
250
+ ticks = 0;
251
+ fenced = false;
252
+ }
253
+ i += run - 1;
254
+ continue;
255
+ }
256
+ if (char === "\n" && ticks > 0 && !fenced) ticks = 0;
257
+ if (char !== "<" || ticks > 0) continue;
258
+ for (const tag of tags) {
259
+ if (!buffer.startsWith(tag, i)) continue;
260
+ return { index: i, length: tag.length, codeTicks: ticks, codeFenced: fenced };
261
+ }
262
+ }
263
+ return undefined;
264
+ }
265
+ }
266
+
155
267
  /** The longest suffix of `text` that is a proper prefix of any tag. */
156
268
  function danglingTagPrefix(text: string): string {
157
269
  const tags = [...OPEN_TAGS, ...CLOSE_TAGS];
@@ -12,77 +12,45 @@
12
12
  * rest as text, mirroring the DSML/OMP scanners used for other families.
13
13
  */
14
14
  import type { InbandScanner, InbandScanResult } from "./inband";
15
+ import { MarkupStreamParser } from "../../markup-stream-parser";
15
16
 
16
17
  const OPEN_TAG = "<tool_call>";
17
18
  const CLOSE_TAG = "</tool_call>";
18
19
 
19
20
  export class QwenToolCallScanner implements InbandScanner {
20
- readonly #allowedNames: ReadonlySet<string>;
21
- #pending = "";
22
- #sequence = 0;
21
+ readonly #parser: MarkupStreamParser<InbandScanResult["calls"][number]>;
23
22
 
24
23
  constructor(allowedNames: ReadonlySet<string>) {
25
- this.#allowedNames = allowedNames;
24
+ this.#parser = new MarkupStreamParser<InbandScanResult["calls"][number]>({
25
+ openers: [OPEN_TAG],
26
+ classifyOpener: (_pending, _openIndex, opener) => {
27
+ if (opener !== OPEN_TAG) return { kind: "literal" };
28
+ return {
29
+ kind: "found",
30
+ closer: CLOSE_TAG,
31
+ parse: (block) => {
32
+ const inner = block.slice(OPEN_TAG.length, -CLOSE_TAG.length).trim();
33
+ const parsed = parseToolCall(inner);
34
+ if (parsed === undefined) return undefined;
35
+ if (!allowedNames.has(parsed.name)) {
36
+ // An undeclared tool is model text, not authority to invoke it.
37
+ return undefined;
38
+ }
39
+ return [parsed];
40
+ },
41
+ callId: (call, _index, seq) => ({ ...call, id: `call_qwen_${seq}` }),
42
+ };
43
+ },
44
+ flush: (pending) => (isPossibleOpener(pending) ? "" : pending),
45
+ });
26
46
  }
27
47
 
28
48
  push(chunk: string): InbandScanResult {
29
- this.#pending += chunk;
30
- let cursor = 0;
31
- let text = "";
32
- const calls: Array<InbandScanResult["calls"][number]> = [];
33
- for (;;) {
34
- const open = this.#pending.indexOf(OPEN_TAG, cursor);
35
- if (open < 0) {
36
- // Hold a possible pending opener at the tail; settle everything before it.
37
- text += this.#pending.slice(cursor, this.#settledEnd(this.#pending.length));
38
- cursor = this.#settledEnd(this.#pending.length);
39
- break;
40
- }
41
- text += this.#pending.slice(cursor, open);
42
- const bodyStart = open + OPEN_TAG.length;
43
- const close = this.#pending.indexOf(CLOSE_TAG, bodyStart);
44
- if (close < 0) {
45
- // Unterminated block: hold from the opener on and settle what preceded it.
46
- cursor = open;
47
- break;
48
- }
49
- const inner = this.#pending.slice(bodyStart, close).trim();
50
- const parsed = parseToolCall(inner);
51
- if (parsed !== undefined) {
52
- if (this.#allowedNames.has(parsed.name)) {
53
- calls.push({
54
- id: `call_qwen_${this.#sequence++}`,
55
- name: parsed.name,
56
- arguments: parsed.arguments,
57
- });
58
- } else {
59
- // An undeclared tool is model text, not authority to invoke it.
60
- text += this.#pending.slice(open, close + CLOSE_TAG.length);
61
- }
62
- } else {
63
- text += this.#pending.slice(open, close + CLOSE_TAG.length);
64
- }
65
- cursor = close + CLOSE_TAG.length;
66
- }
67
- this.#pending = this.#pending.slice(cursor);
68
- return { text, calls };
49
+ return this.#parser.push(chunk);
69
50
  }
70
51
 
71
52
  flush(): InbandScanResult {
72
- const pending = this.#pending;
73
- this.#pending = "";
74
- if (isPossibleOpener(pending)) return { text: "", calls: [] };
75
- return { text: pending, calls: [] };
76
- }
77
-
78
- #settledEnd(end: number): number {
79
- // Keep a trailing fragment that could be the start of an opener.
80
- const tail = this.#pending.slice(0, end);
81
- const maxPrefix = Math.min(OPEN_TAG.length - 1, tail.length);
82
- for (let len = maxPrefix; len > 0; len -= 1) {
83
- if (OPEN_TAG.startsWith(tail.slice(-len))) return end - len;
84
- }
85
- return end;
53
+ return this.#parser.flush();
86
54
  }
87
55
  }
88
56
 
@@ -158,6 +158,21 @@ const PARAM_QUIRKS: readonly ParamQuirk[] = [
158
158
  // confirms that the selected-model request must not carry temperature.
159
159
  stripControls: ["temperature"],
160
160
  },
161
+ {
162
+ provider: "nvidia",
163
+ // NVIDIA NIM rejects a set of cross-wire fields with 400 even though the
164
+ // OpenAI-compatible chat wire permits them. Stripping them here keeps the
165
+ // dispatch on the same wire shape the upstream accepts.
166
+ stripControls: [
167
+ "extension:client_metadata",
168
+ "extension:prompt_cache_key",
169
+ "service_tier",
170
+ "extension:anthropic_beta",
171
+ "extension:thinking_budget",
172
+ "extension:metadata_user_id",
173
+ "extension:custom_field",
174
+ ],
175
+ },
161
176
  {
162
177
  provider: "anthropic",
163
178
  matchModel: rejectsSamplingParams,
@@ -9,7 +9,7 @@ import { GatewayError } from '../gateway-error';
9
9
  import { applyRouteUserAgent, type ProviderDispatchContext } from "../../providers/provider-contracts";
10
10
  import { abortGatewayError, createUpstreamDeadlineLifecycle } from '../../providers/operations/upstream-deadline';
11
11
  import { mapClaudeHttpError } from '../messages-errors';
12
- import { isRecord } from '../codecs';
12
+ import { isRecord, toolAliasesFor } from '../codecs';
13
13
  import { upstreamMessageAsIs, MAX_UPSTREAM_ERROR_BYTES } from '../../transport/failure-policy';
14
14
  import { parseClaudeSseStream, claudeResponseToEvents } from '../response/messages';
15
15
 
@@ -60,7 +60,7 @@ export async function* sendClaudeMessagesRequest(
60
60
  {},
61
61
  "upstream",
62
62
  );
63
- yield* parseClaudeSseStream(response.body, context.abort_signal, isOAuth);
63
+ yield* parseClaudeSseStream(response.body, context.abort_signal, isOAuth, toolAliasesFor(request));
64
64
  return;
65
65
  }
66
66
  const body = await response.text().catch(() => "");
@@ -17,6 +17,7 @@ import { resolveDialect } from "../../protocol/translation/dialect/resolver";
17
17
  import { resolveThinkingForModel } from "../../protocol/translation/dialect/thinking-ladder";
18
18
  import type { DialectPolicy } from "../../protocol/translation/dialect/types";
19
19
  import { postUpstreamJson } from "../../protocol/upstream/post-json";
20
+ import { gzipSync } from "node:zlib";
20
21
  import { GATEWAY_USER_AGENT } from "../operations/gateway-user-agent";
21
22
  import { resolvePromptCacheKey } from "../operations/session-resolution";
22
23
  import {
@@ -28,6 +29,24 @@ import {
28
29
  } from "../../protocol/codecs";
29
30
  const TEXT_DECODER = new TextDecoder("utf-8", { fatal: false });
30
31
 
32
+ /**
33
+ * Whether an upstream error body is the channel-check rejection the CodeBuddy
34
+ * console fallback recovers from. Ported from the donor gateway
35
+ * (`codebuddy2api/upstream_compat.is_channel_check_error`): it requires both
36
+ * a channel-check business code (`11128` / `11-128`) and one of the
37
+ * explanatory texts. A bare code match swept unrelated failures into a retry.
38
+ */
39
+ export function isBuddyChannelCheckError(raw: string): boolean {
40
+ if (!/code["']?\s*:\s*["']?(?:11128|11-128)/i.test(raw)) return false;
41
+ const lower = raw.toLowerCase();
42
+ return (
43
+ lower.includes("渠道校验") || // 渠道校验
44
+ lower.includes("channel verification") ||
45
+ lower.includes("official client") ||
46
+ lower.includes("illegal api invocation from an unapproved channel")
47
+ );
48
+ }
49
+
31
50
  /**
32
51
  * Provider ids that understand the OpenAI-style `service_tier` control. Only
33
52
  * these upstreams accept the field; every other OpenAI-compatible provider
@@ -111,8 +130,21 @@ export interface OpenAICompatibleAdapterConfig {
111
130
  * stamps `Content-Encoding: zstd` — the Grok Build CLI does this via its
112
131
  * sampler (`request_compression.rs`), and the proxy accepts it (verified
113
132
  * live 2026-11-02). Falls back to plain JSON if the runtime lacks zstd.
133
+ * `"gzip"` compresses with `gzipSync` and stamps `Content-Encoding: gzip`
134
+ * — the CodeBuddy CLI contract (donor `codebuddy2api`): the CLI profiles
135
+ * always compress; the CN desktop profile compresses requests >= 1024 B.
136
+ * The caller must also send `Accept-Encoding: identity` (the compressed
137
+ * body and the uncompressed response are independent choices).
138
+ */
139
+ readonly request_compression?: "json" | "zstd" | "gzip";
140
+ /**
141
+ * One retry against this path when the primary chat endpoint answers a
142
+ * channel-check 400 (`11128` / `11-128`). Matches the donor gateway's
143
+ * `/console/chat/completions` fallback for the international profile: the
144
+ * primary `v2` endpoint rejects requests that do not carry the channel
145
+ * marker, and a single console retry is the reference's recovery.
114
146
  */
115
- readonly request_compression?: "json" | "zstd";
147
+ readonly channel_fallback_path?: string;
116
148
  /**
117
149
  * Stamps `user-agent: Cartethyia/<version>` on every dispatch. Explicit
118
150
  * opt-in per provider spec — never a default — so first-party cloaking
@@ -216,6 +248,13 @@ export class OpenAICompatibleAdapter extends BaseProviderAdapter {
216
248
  // here — alongside the other request headers — so auth/proxy layers see the
217
249
  // final shape before the transport runs.
218
250
  if (this.config.request_compression === "zstd") headers["content-encoding"] = "zstd";
251
+ else if (this.config.request_compression === "gzip") {
252
+ headers["content-encoding"] = "gzip";
253
+ // The compressed request and the response encoding are independent; the
254
+ // reference requests the identity response encoding so the stream arrives
255
+ // uncompressed (matching `Accept-Encoding: identity` in the donor).
256
+ headers["accept-encoding"] = "identity";
257
+ }
219
258
  Object.assign(headers, filterProviderCustomHeaders(context.credential.custom_headers));
220
259
  applyRouteUserAgent(headers, context.credential, context.user_agent);
221
260
  return headers;
@@ -271,6 +310,11 @@ export class OpenAICompatibleAdapter extends BaseProviderAdapter {
271
310
  // plus `message_delta.usage` — so nothing is lost by omitting it.
272
311
  const existing = payload["stream_options"] as Record<string, unknown> | undefined;
273
312
  payload["stream_options"] = { ...(existing ?? {}), include_usage: true };
313
+ } else if (!effectiveRequest.stream && payload["stream_options"] !== undefined) {
314
+ // A non-streaming request must not carry `stream_options`: several
315
+ // OpenAI-compatible upstreams (NVIDIA NIM, DeepSeek) reject the body
316
+ // with 400 "Stream options can only be defined when stream=True".
317
+ delete payload["stream_options"];
274
318
  }
275
319
  // Apply structured_output configuration
276
320
  if (this.config.structured_output?.enabled) {
@@ -330,18 +374,38 @@ export class OpenAICompatibleAdapter extends BaseProviderAdapter {
330
374
  return u.toString();
331
375
  })();
332
376
  const outboundFetch = context.outbound_fetch ?? this.config.fetch ?? globalThis.fetch;
333
- const upstream = await postUpstreamJson(
334
- fetchUrl,
335
- headers,
336
- payload as Record<string, unknown>,
337
- context,
338
- outboundFetch,
339
- // zstd body encoding: matches the Grok Build CLI's
340
- // `request_compression` (zstd with a JSON fallback when unavailable).
377
+ const bodyEncoder =
341
378
  this.config.request_compression === "zstd" && typeof Bun?.zstdCompressSync === "function"
342
- ? (p) => Bun.zstdCompressSync(Buffer.from(JSON.stringify(p))) as Uint8Array<ArrayBuffer>
343
- : undefined,
344
- );
379
+ ? (p: Record<string, unknown>) => Bun.zstdCompressSync(Buffer.from(JSON.stringify(p))) as Uint8Array<ArrayBuffer>
380
+ : this.config.request_compression === "gzip"
381
+ ? (p: Record<string, unknown>) => gzipSync(Buffer.from(JSON.stringify(p)), { level: 6 })
382
+ : undefined;
383
+ const channelFallback =
384
+ this.config.channel_fallback_path !== undefined &&
385
+ endpointPath !== this.config.channel_fallback_path
386
+ ? joinUrl(this.config.resolveBaseUrl?.(context) ?? this.config.base_url, this.config.channel_fallback_path)
387
+ : undefined;
388
+ const upstream = await postUpstreamJson(fetchUrl, headers, payload as Record<string, unknown>, context, outboundFetch, bodyEncoder);
389
+ if (channelFallback !== undefined && upstream.res.status === 400) {
390
+ // Read the body only for a 400 that could still be retried. The channel
391
+ // check is the one refusal the console endpoint recovers from; a rejected
392
+ // body is consumed here so the fallback attempt carries a fresh stream.
393
+ const raw = await upstream.res.text().catch(() => "");
394
+ upstream.release();
395
+ if (isBuddyChannelCheckError(raw)) {
396
+ const retry = await postUpstreamJson(channelFallback, headers, payload as Record<string, unknown>, context, outboundFetch, bodyEncoder);
397
+ retry.release();
398
+ return retry.res;
399
+ }
400
+ // Not a channel check — reconstruct the 400 response so downstream error
401
+ // classification still sees the upstream's body. A `Response` body can be
402
+ // consumed once, so rebuild it from the captured text.
403
+ return new Response(raw, {
404
+ status: 400,
405
+ statusText: upstream.res.statusText,
406
+ headers: { "content-type": "application/json" },
407
+ });
408
+ }
345
409
  upstream.release();
346
410
  return upstream.res;
347
411
  }