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
@@ -0,0 +1,195 @@
1
+ /**
2
+ * Generic streaming prefix-buffer parser for in-band tool-call markup.
3
+ *
4
+ * Three grammars (DSML blocks, OMP `<tool_calls:8hex>` envelopes, Qwen
5
+ * `<tool_call>` JSON blocks) share one stateful loop: append chunks to a
6
+ * pending buffer, scan for the earliest opener, settle visible text up to a
7
+ * safe end that holds back a possible trailing opener fragment, parse a
8
+ * complete block when its closer appears, fail open past a block-size cap, and
9
+ * flush the tail. The grammar-specific parts are supplied as configuration:
10
+ * opener strings, an opener classifier (some grammars must validate beyond the
11
+ * bare prefix, e.g. OMP's hex marker), closer/parse functions, an unclosed
12
+ * block policy (hold vs fail-open pass-through), a flush policy (pass through
13
+ * vs discard an unterminated envelope), and a call-id factory. Each parser
14
+ * instance keeps its own buffer; nothing is shared.
15
+ */
16
+
17
+ /** Result of one push/flush pass: text that settled plus recovered calls. */
18
+ export interface MarkupStreamParseResult<Call extends { readonly id: string }> {
19
+ readonly text: string;
20
+ readonly calls: readonly Call[];
21
+ }
22
+
23
+ /** Raw call shape produced by a grammar's block parser, without its id. */
24
+ export type MarkupRawCall<Call extends { readonly id: string }> = Omit<Call, "id">;
25
+
26
+ /** Builds a complete call (id included) from a parsed raw call. */
27
+ export type MarkupCallFactory<Call extends { readonly id: string }> = (
28
+ call: MarkupRawCall<Call>,
29
+ index: number,
30
+ seq: number,
31
+ ) => Call;
32
+
33
+ /** Classification of one opener candidate occurrence. */
34
+ export type MarkupOpenerClassification<Call extends { readonly id: string }> =
35
+ | {
36
+ /**
37
+ * The candidate may be the start of a real opener (e.g. a partial hex
38
+ * marker): hold everything from the candidate until a later chunk
39
+ * completes or disproves it.
40
+ */
41
+ readonly kind: "hold";
42
+ }
43
+ | {
44
+ /**
45
+ * The candidate is definitely not an opener: emit it as literal text
46
+ * (one character) and rescan after it.
47
+ */
48
+ readonly kind: "literal";
49
+ }
50
+ | {
51
+ readonly kind: "found";
52
+ /** Tag that closes the block opened at this position. */
53
+ readonly closer: string;
54
+ /**
55
+ * Parses one complete block (opener through closer). `undefined` means
56
+ * the block is not valid markup and is passed through as text verbatim.
57
+ */
58
+ readonly parse: (block: string) => readonly MarkupRawCall<Call>[] | undefined;
59
+ /**
60
+ * Call factory for the calls recovered from this block; defaults to the
61
+ * parser-level `callId`. Some grammars derive ids from block-local state
62
+ * (e.g. OMP embeds the envelope marker).
63
+ */
64
+ readonly callId?: MarkupCallFactory<Call>;
65
+ };
66
+
67
+ export interface MarkupStreamParserOptions<Call extends { readonly id: string }> {
68
+ /**
69
+ * Opener frontier. The earliest occurrence wins; trailing fragments that
70
+ * could start one are held back from settled text.
71
+ */
72
+ readonly openers: readonly string[];
73
+ /** Classifies an opener candidate found at a scan position. */
74
+ readonly classifyOpener: (
75
+ pending: string,
76
+ index: number,
77
+ opener: string,
78
+ ) => MarkupOpenerClassification<Call>;
79
+ /**
80
+ * Policy when a block's closer has not arrived yet. Default `"hold"`: keep
81
+ * the whole pending buffer from the opener until a later chunk completes or
82
+ * disproves the block. Returning `{ kind: "passThrough" }` fails open:
83
+ * everything from the opener is emitted as text immediately (the DSML
84
+ * block-size cap).
85
+ */
86
+ readonly onUnclosed?: (
87
+ pending: string,
88
+ openIndex: number,
89
+ ) => "hold" | { readonly kind: "passThrough" };
90
+ /**
91
+ * Tail policy at flush. Returns the text to emit for whatever remains in the
92
+ * buffer (default: everything, i.e. pass through). A grammar that discards
93
+ * unterminated envelopes returns `""` when the tail looks like an opener.
94
+ */
95
+ readonly flush?: (pending: string) => string;
96
+ /** Call factory for recovered calls, unless a block overrides it. */
97
+ readonly callId?: MarkupCallFactory<Call>;
98
+ }
99
+
100
+ export class MarkupStreamParser<Call extends { readonly id: string }> {
101
+ readonly #options: MarkupStreamParserOptions<Call>;
102
+ #pending = "";
103
+ #callSequence = 0;
104
+
105
+ constructor(options: MarkupStreamParserOptions<Call>) {
106
+ this.#options = options;
107
+ }
108
+
109
+ /** Feeds one chunk and returns only settled text and complete calls. */
110
+ push(chunk: string): MarkupStreamParseResult<Call> {
111
+ this.#pending += chunk;
112
+ let cursor = 0;
113
+ let visible = "";
114
+ const calls: Call[] = [];
115
+ for (;;) {
116
+ let openIndex = -1;
117
+ let opener = "";
118
+ for (const candidate of this.#options.openers) {
119
+ const index = this.#pending.indexOf(candidate, cursor);
120
+ if (index >= 0 && (openIndex < 0 || index < openIndex)) {
121
+ openIndex = index;
122
+ opener = candidate;
123
+ }
124
+ }
125
+ if (openIndex < 0) {
126
+ const safeEnd = this.#settledEnd(cursor);
127
+ visible += this.#pending.slice(cursor, safeEnd);
128
+ cursor = safeEnd;
129
+ break;
130
+ }
131
+ visible += this.#pending.slice(cursor, openIndex);
132
+ const classified = this.#options.classifyOpener(this.#pending, openIndex, opener);
133
+ if (classified.kind === "literal") {
134
+ visible += "<";
135
+ cursor = openIndex + 1;
136
+ continue;
137
+ }
138
+ if (classified.kind === "hold") {
139
+ cursor = openIndex;
140
+ break;
141
+ }
142
+ const closeIndex = this.#pending.indexOf(classified.closer, openIndex + opener.length);
143
+ if (closeIndex < 0) {
144
+ const decision = this.#options.onUnclosed?.(this.#pending, openIndex) ?? "hold";
145
+ if (decision === "hold") {
146
+ cursor = openIndex;
147
+ break;
148
+ }
149
+ visible += this.#pending.slice(openIndex);
150
+ cursor = this.#pending.length;
151
+ break;
152
+ }
153
+ const end = closeIndex + classified.closer.length;
154
+ const block = this.#pending.slice(openIndex, end);
155
+ const parsed = classified.parse(block);
156
+ if (parsed === undefined) {
157
+ visible += block;
158
+ } else {
159
+ const callFor = classified.callId ?? this.#options.callId;
160
+ if (callFor === undefined) {
161
+ throw new Error("MarkupStreamParser: no call factory configured");
162
+ }
163
+ parsed.forEach((call, index) => {
164
+ calls.push(callFor(call, index, this.#callSequence++));
165
+ });
166
+ }
167
+ cursor = end;
168
+ }
169
+ this.#pending = this.#pending.slice(cursor);
170
+ return { text: visible, calls };
171
+ }
172
+
173
+ /** Settles trailing text per the configured flush policy. */
174
+ flush(): MarkupStreamParseResult<Call> {
175
+ const text = this.#options.flush?.(this.#pending) ?? this.#pending;
176
+ this.#pending = "";
177
+ return { text, calls: [] };
178
+ }
179
+
180
+ /** Holds back a trailing fragment that could be the start of an opener. */
181
+ #settledEnd(start: number): number {
182
+ const remaining = this.#pending.slice(start);
183
+ const maxOpener = this.#options.openers.reduce(
184
+ (max, open) => Math.max(max, open.length - 1),
185
+ 0,
186
+ );
187
+ for (let length = Math.min(maxOpener, remaining.length); length > 0; length -= 1) {
188
+ const suffix = remaining.slice(-length);
189
+ if (this.#options.openers.some((open) => open.startsWith(suffix))) {
190
+ return this.#pending.length - length;
191
+ }
192
+ }
193
+ return this.#pending.length;
194
+ }
195
+ }
@@ -28,3 +28,22 @@ export function reasoningTextOf(part: ReasoningTextSource): string | undefined {
28
28
  if (typeof summary === "string" && summary.length > 0) return summary;
29
29
  return undefined;
30
30
  }
31
+
32
+ /**
33
+ * The one reader for reasoning text replayed to upstream providers.
34
+ *
35
+ * Request builders echo the previous turn's reasoning back to a provider, and
36
+ * they deliberately prefer `summary` (the digest) ahead of `payload` (the
37
+ * stream) — the opposite of `reasoningTextOf`, which is for display. Returning
38
+ * `undefined` when neither field is a string lets each call site keep its exact
39
+ * fallback ("" for the chat/messages/gemini builders, the raw payload for
40
+ * Qoder's echo), so upstream-visible bytes do not change. Opaque parts are not
41
+ * special-cased here: the replay readers never consulted `opaque` either.
42
+ */
43
+ export function reasoningTextForReplay(part: ReasoningTextSource): string | undefined {
44
+ const summary = part.summary;
45
+ if (typeof summary === "string") return summary;
46
+ const payload = part.payload;
47
+ if (typeof payload === "string") return payload;
48
+ return undefined;
49
+ }
@@ -16,6 +16,7 @@ import { clampThinkingLevelForModel } from "../../protocol/translation/dialect/m
16
16
  import { isClaudeBillingHeaderText, resolveImageSource } from "../codecs";
17
17
  import { resolvePromptCacheKey } from "../../providers/operations/session-resolution";
18
18
  import { reasoningEffortFromIntent } from "../../protocol/reasoning";
19
+ import { reasoningTextForReplay } from "../reasoning-text";
19
20
  import { parseChatResponseToEvents, decodeChatSseStream } from "../response/chat";
20
21
 
21
22
  /** Fallback when no resolved policy is supplied — today's exact behavior. */
@@ -220,6 +221,13 @@ export function canonicalToChatPayload(
220
221
  }
221
222
  }
222
223
  for (const m of request.messages) {
224
+ // Reasoning parts and their presence are computed once per message and
225
+ // shared by every branch below; the presence gate is on the *existence* of
226
+ // a reasoning part, not on non-empty text (see the comment at the first use).
227
+ const reasoningParts = m.content.filter(
228
+ (c): c is Extract<typeof c, { kind: "reasoning" }> => c.kind === "reasoning",
229
+ );
230
+ const hasReasoning = reasoningParts.length > 0;
223
231
  if (m.role === "assistant" && m.content.some((c) => c.kind === "toolCall")) {
224
232
  const toolCalls = m.content
225
233
  .filter((c): c is Extract<typeof c, { kind: "toolCall" }> => c.kind === "toolCall")
@@ -251,16 +259,12 @@ export function canonicalToChatPayload(
251
259
  // thinking mode" (CodeBuddy 400). Chat Completions has no
252
260
  // reasoning content part, so it travels in the provider-native
253
261
  // `reasoning_content` side channel, same as the branches below.
254
- const reasoningContent = m.content
255
- .filter((c): c is Extract<typeof c, { kind: "reasoning" }> => c.kind === "reasoning")
256
- .map((c) => c.summary ?? (typeof c.payload === "string" ? c.payload : ""))
257
- .join("\n");
262
+ const reasoningContent = reasoningParts.map((c) => reasoningTextForReplay(c) ?? "").join("\n");
258
263
  // Gated on the *presence* of a reasoning part, not on non-empty text:
259
264
  // with `display: "omitted"` the provider emits a thinking block whose
260
265
  // text is empty but whose signature marks the turn as a thinking turn,
261
266
  // and the upstream still demands the field back. Omitting it there
262
267
  // reproduces the same 400, so an empty string is the correct value.
263
- const hasReasoning = m.content.some((c) => c.kind === "reasoning");
264
268
  messages.push({
265
269
  role: "assistant",
266
270
  content: effectiveContent,
@@ -295,13 +299,9 @@ export function canonicalToChatPayload(
295
299
  content: p.is_error === true ? `[tool_error] ${resultContent}` : resultContent,
296
300
  });
297
301
  }
298
- const reasoningContent = m.content
299
- .filter((c): c is Extract<typeof c, { kind: "reasoning" }> => c.kind === "reasoning")
300
- .map((c) => c.summary ?? (typeof c.payload === "string" ? c.payload : ""))
301
- .join("\n");
302
+ const reasoningContent = reasoningParts.map((c) => reasoningTextForReplay(c) ?? "").join("\n");
302
303
  // Presence, not length: an `omitted`-display thinking block carries an
303
304
  // empty text with a signature, and the field is still required back.
304
- const hasReasoning = m.content.some((c) => c.kind === "reasoning");
305
305
  const hasRichContent = m.content.some(
306
306
  (c) => c.kind === "image" || c.kind === "file" || c.kind === "audio" || c.kind === "document",
307
307
  );
@@ -15,6 +15,7 @@ import {
15
15
  type RewriteWalk,
16
16
  } from "../codecs";
17
17
  import { geminiThinkingOutputFloor } from "../../protocol/reasoning";
18
+ import { reasoningTextForReplay } from "../reasoning-text";
18
19
  import { base64DecodedByteLength, clampImage } from "../image-clamp";
19
20
 
20
21
  export function geminiModelUrl(base: string, model: string, action: string): string {
@@ -151,8 +152,13 @@ function toGeminiPart(
151
152
  ): Record<string, unknown>[] {
152
153
  if (part.kind === "text") return [{ text: part.text }];
153
154
  if (part.kind === "reasoning") {
154
- const summary = part.summary ?? (typeof part.payload === "string" ? part.payload : "");
155
- return [{ text: summary, thought: true, ...(part.signature ? { thoughtSignature: part.signature } : {}) }];
155
+ // A `thinking` part must carry a non-empty signature, or Gemini-family
156
+ // upstreams reject the whole request with `400 ... thinking.signature:
157
+ // Field required`. Drop unsigned reasoning blocks (missing *or* empty
158
+ // signature) so the conversation replays cleanly.
159
+ if (typeof part.signature !== "string" || part.signature.length === 0) return [];
160
+ const summary = reasoningTextForReplay(part) ?? "";
161
+ return [{ text: summary, thought: true, thoughtSignature: part.signature }];
156
162
  }
157
163
  if (part.kind === "image") {
158
164
  // Resolution goes through the shared `resolveImageSource` primitive rather
@@ -17,16 +17,19 @@ import {
17
17
  normalizeAnthropicToolCallId,
18
18
  parseArguments,
19
19
  prefixClaudeToolName,
20
+ recordToolAliases,
20
21
  sanitizeSchemaForAnthropic,
21
22
  stringValue,
22
23
  toWellFormedDeep,
23
24
  toWellFormedString,
24
25
  type ClaudeWireObject,
26
+ type ToolNameAlias,
25
27
  isRecord,
26
28
  resolveImageSource,
27
29
  splitDataUrl,
28
30
  finiteNumber,
29
31
  } from "../codecs";
32
+ import { reasoningTextForReplay } from "../reasoning-text";
30
33
  import { log } from "../../observability/logger";
31
34
  import { base64DecodedByteLength, clampImage } from "../image-clamp";
32
35
  import { LEVEL_TO_BUDGET } from "../../protocol/translation/thinking";
@@ -286,6 +289,7 @@ function partToClaudeBlock(
286
289
  idMap: Map<string, string>;
287
290
  isOAuth: boolean;
288
291
  imageCount: number;
292
+ toolAliases: ToolNameAlias[];
289
293
  maxBytesOverride?: number;
290
294
  },
291
295
  ): ClaudeWireObject | undefined {
@@ -335,10 +339,12 @@ function partToClaudeBlock(
335
339
  // stamp every call with its array position, so copying it here leaked a
336
340
  // stream-only field into a request. The position is not lost: the block
337
341
  // order in `content[]` already expresses it.
342
+ const sentName = prefixClaudeToolName(part.name, ctx?.isOAuth ?? false);
343
+ if (sentName !== part.name) ctx?.toolAliases.push({ sent: sentName, original: part.name });
338
344
  return {
339
345
  type: "tool_use",
340
346
  id: normalized,
341
- name: prefixClaudeToolName(part.name, ctx?.isOAuth ?? false),
347
+ name: sentName,
342
348
  input: toWellFormedDeep(parseArguments(part.arguments)),
343
349
  ...(part.namespace === undefined ? {} : { namespace: part.namespace }),
344
350
  };
@@ -374,7 +380,7 @@ function partToClaudeBlock(
374
380
  !part.opaque &&
375
381
  (typeof part.signature !== "string" || part.signature.trim().length === 0)
376
382
  ) {
377
- const text = part.summary ?? (stringValue(part.payload) ? part.payload : "");
383
+ const text = reasoningTextForReplay(part) ?? "";
378
384
  return text.length > 0 ? { type: "text", text: toWellFormedString(text) } : undefined;
379
385
  }
380
386
  if (part.opaque) {
@@ -384,9 +390,7 @@ function partToClaudeBlock(
384
390
  }
385
391
  return {
386
392
  type: "thinking",
387
- thinking: toWellFormedString(
388
- part.summary ?? (stringValue(part.payload) ? part.payload : ""),
389
- ),
393
+ thinking: toWellFormedString(reasoningTextForReplay(part) ?? ""),
390
394
  ...(part.signature === undefined
391
395
  ? {}
392
396
  : { signature: toWellFormedString(part.signature) }),
@@ -578,6 +582,7 @@ export function canonicalToClaudeMessagesPayload(
578
582
  idMap,
579
583
  isOAuth: options.isOAuth ?? false,
580
584
  imageCount,
585
+ toolAliases: [] as ToolNameAlias[],
581
586
  ...(options.maxBytesOverride === undefined
582
587
  ? {}
583
588
  : { maxBytesOverride: options.maxBytesOverride }),
@@ -756,27 +761,34 @@ export function canonicalToClaudeMessagesPayload(
756
761
  });
757
762
  }
758
763
  if (request.tools?.length) {
759
- payload.tools = request.tools.map((tool) => ({
760
- name: prefixClaudeToolName(tool.name, isOAuth),
761
- ...(tool.description === undefined
762
- ? {}
763
- : { description: toWellFormedDeep(tool.description) }),
764
- input_schema: sanitizeSchemaForAnthropic(tool.jsonSchema),
765
- ...(tool.strict === true ? { strict: true } : {}),
766
- ...(tool.eager_input_streaming === undefined
767
- ? {}
768
- : { eager_input_streaming: tool.eager_input_streaming }),
769
- ...(tool.defer_loading === undefined
770
- ? {}
771
- : { defer_loading: tool.defer_loading }),
772
- ...(tool.input_examples === undefined ? {} : { input_examples: tool.input_examples }),
773
- ...(tool.allowed_callers === undefined ? {} : { allowed_callers: tool.allowed_callers }),
774
- }));
764
+ payload.tools = request.tools.map((tool) => {
765
+ const sentName = prefixClaudeToolName(tool.name, isOAuth);
766
+ if (sentName !== tool.name) ctx.toolAliases.push({ sent: sentName, original: tool.name });
767
+ return {
768
+ name: sentName,
769
+ ...(tool.description === undefined
770
+ ? {}
771
+ : { description: toWellFormedDeep(tool.description) }),
772
+ input_schema: sanitizeSchemaForAnthropic(tool.jsonSchema),
773
+ ...(tool.strict === true ? { strict: true } : {}),
774
+ ...(tool.eager_input_streaming === undefined
775
+ ? {}
776
+ : { eager_input_streaming: tool.eager_input_streaming }),
777
+ ...(tool.defer_loading === undefined
778
+ ? {}
779
+ : { defer_loading: tool.defer_loading }),
780
+ ...(tool.input_examples === undefined ? {} : { input_examples: tool.input_examples }),
781
+ ...(tool.allowed_callers === undefined ? {} : { allowed_callers: tool.allowed_callers }),
782
+ };
783
+ });
775
784
  }
776
785
  const rawToolChoice = canonicalToolChoice(request.tool_choice);
777
786
  if (isRecord(rawToolChoice) && rawToolChoice.type === "tool" && stringValue(rawToolChoice.name)) {
778
- rawToolChoice.name = prefixClaudeToolName(rawToolChoice.name, isOAuth);
787
+ const sentName = prefixClaudeToolName(rawToolChoice.name, isOAuth);
788
+ if (sentName !== rawToolChoice.name) ctx.toolAliases.push({ sent: sentName, original: rawToolChoice.name });
789
+ rawToolChoice.name = sentName;
779
790
  }
791
+ recordToolAliases(request, ctx.toolAliases);
780
792
  const parallelToolCallsDisabled =
781
793
  request.generation_controls.parallel_tool_calls === false;
782
794
  const supportsToolChoice = options.dialectPolicy?.supportsToolChoice ?? true;
@@ -9,7 +9,7 @@ import { gatewayErrorFromStreamError } from "../stream-error-frames";
9
9
  import { mergeResponsesUsage } from "./responses";
10
10
  import { usageFromProvider, readReasoningText } from "../../providers/provider-usage";
11
11
  import { createToolEmitLedger } from "../tool-identity";
12
- import { ThinkSplitter, stripThinkingTags } from "../think-split";
12
+ import { ThinkingTagStripper, ThinkSplitter, stripThinkingTags } from "../think-split";
13
13
  import { DsmlStreamParser, parseDsmlToolCalls } from "../dsml";
14
14
  import {
15
15
  OmpToolCallMarkupParser,
@@ -267,10 +267,13 @@ export async function* decodeChatSseStream(
267
267
  // Single emission ledger: one entry per logical call.
268
268
  const ledger = createToolEmitLedger();
269
269
  const think = new ThinkSplitter();
270
- // Separate splitter for the reasoning channel: `reasoning_content` is its own
270
+ // Separate stripper for the reasoning channel: `reasoning_content` is its own
271
271
  // stream, and interleaving it into the answer splitter would let a tag in one
272
- // channel swallow the other's text.
273
- const reasoningThink = new ThinkSplitter();
272
+ // channel swallow the other's text. The reasoning channel is thought in its
273
+ // entirety — untagged prose included — so it strips paired tags statefully
274
+ // while preserving source order instead of splitting into text/reasoning
275
+ // buckets that a `${reasoning}${text}` join would reorder.
276
+ const reasoningThink = new ThinkingTagStripper();
274
277
  const declaredToolNames = declaredToolNamesForRequest(request);
275
278
  const callableToolNames = callableToolNamesForRequest(request);
276
279
  const contentMarkup =
@@ -364,21 +367,13 @@ export async function* decodeChatSseStream(
364
367
  typeof reasoningRaw === "string" ? reasoningMarkup?.push(reasoningRaw) : undefined;
365
368
  // Strip stray `<think>` tags the upstream double-wrapped inside its own
366
369
  // reasoning stream (buddy's hy4-preview-f leaks them per-delta). This
367
- // is the splitter's job, not `stripThinkingTags`'s: a stateless strip
370
+ // is the stripper's job, not `stripThinkingTags`'s: a stateless strip
368
371
  // cannot see a tag split across two deltas, so `"<thi"` was emitted
369
372
  // verbatim and only the following `</think>` removed.
370
- const reasoningSplit =
373
+ const reasoningText =
371
374
  typeof reasoningRaw === "string"
372
375
  ? reasoningThink.push(reasoningMarkupResult?.text ?? reasoningRaw)
373
376
  : undefined;
374
- // Both sides are reasoning. The splitter routes text outside a
375
- // `<think>` span to its `.text` side, but this field is the reasoning
376
- // stream in its entirety: untagged prose here is still thought, not
377
- // answer. Taking only `.reasoning` dropped every un-tagged delta.
378
- const reasoningText =
379
- reasoningSplit === undefined
380
- ? undefined
381
- : `${reasoningSplit.reasoning}${reasoningSplit.text}`;
382
377
  if (typeof reasoningText === "string" && reasoningText.length > 0) {
383
378
  yield {
384
379
  type: "content_delta",
@@ -538,9 +533,7 @@ export async function* decodeChatSseStream(
538
533
  }
539
534
  const reasoningMarkupTail = reasoningMarkup?.flush();
540
535
  if (reasoningMarkupTail?.text) {
541
- const split = reasoningThink.push(reasoningMarkupTail.text);
542
- // Both sides are reasoning: see the per-delta branch above.
543
- const reasoningText = `${split.reasoning}${split.text}`;
536
+ const reasoningText = reasoningThink.push(reasoningMarkupTail.text);
544
537
  if (reasoningText.length > 0) {
545
538
  yield {
546
539
  type: "content_delta",
@@ -549,10 +542,9 @@ export async function* decodeChatSseStream(
549
542
  } as CanonicalEvent;
550
543
  }
551
544
  }
552
- // Release anything the reasoning splitter held for a tag that never
545
+ // Release anything the reasoning stripper held for a tag that never
553
546
  // completed, before `think.flush()` and the terminal.
554
- const reasoningRest = reasoningThink.flush();
555
- const reasoningTail = `${reasoningRest.reasoning}${reasoningRest.text}`;
547
+ const reasoningTail = reasoningThink.flush();
556
548
  if (reasoningTail.length > 0) {
557
549
  yield {
558
550
  type: "content_delta",
@@ -12,12 +12,33 @@ import { usageFromProvider } from '../../providers/provider-usage';
12
12
  import { mapClaudeStreamError } from '../messages-errors';
13
13
  import {
14
14
  finiteNumber,
15
+ restoreAliasedToolName,
15
16
  stringValue,
17
+ toolAliasesFor,
16
18
  unprefixClaudeToolName,
17
19
  type ClaudeWireObject,
20
+ type ToolNameAlias,
18
21
  isRecord,
19
22
  } from '../codecs';
20
23
 
24
+ /**
25
+ * Restore a tool name from the wire to the caller's original, exactly.
26
+ *
27
+ * A name the builder renamed (recorded in `aliases`) is restored verbatim; any
28
+ * other name falls back to the OAuth underscore-unprefixing rule. This is the
29
+ * single restoration point for the Claude wire, so the two rules can never
30
+ * disagree between the stream and non-stream decoders.
31
+ */
32
+ function restoreClaudeToolName(
33
+ sent: string,
34
+ isOAuth: boolean,
35
+ aliases: ReadonlyArray<ToolNameAlias>,
36
+ ): string {
37
+ const restored = restoreAliasedToolName(sent, aliases);
38
+ if (restored !== sent) return restored;
39
+ return unprefixClaudeToolName(sent, isOAuth);
40
+ }
41
+
21
42
  export function mapClaudeStopReason(raw: string): CanonicalStopReason {
22
43
  switch (raw) {
23
44
  case "end_turn":
@@ -38,7 +59,7 @@ export function mapClaudeStopReason(raw: string): CanonicalStopReason {
38
59
  }
39
60
  }
40
61
 
41
- export function parseClaudeContent(value: unknown, isOAuth = false): ContentPart {
62
+ export function parseClaudeContent(value: unknown, isOAuth = false, aliases: ReadonlyArray<ToolNameAlias> = []): ContentPart {
42
63
  if (stringValue(value)) return { kind: "text", text: value };
43
64
  if (!isRecord(value))
44
65
  return { kind: "extension", name: "messages:unknown", payload: value };
@@ -59,7 +80,7 @@ export function parseClaudeContent(value: unknown, isOAuth = false): ContentPart
59
80
  return {
60
81
  kind: "toolCall",
61
82
  call_id: value.id,
62
- name: unprefixClaudeToolName(value.name, isOAuth),
83
+ name: restoreClaudeToolName(value.name, isOAuth, aliases),
63
84
  arguments: value.input ?? {},
64
85
  ...(finiteNumber(value.index) ? { index: value.index } : {}),
65
86
  };
@@ -77,7 +98,7 @@ export function parseClaudeContent(value: unknown, isOAuth = false): ContentPart
77
98
  const content = stringValue(value.content)
78
99
  ? value.content
79
100
  : Array.isArray(value.content)
80
- ? value.content.map((content) => parseClaudeContent(content, isOAuth))
101
+ ? value.content.map((content) => parseClaudeContent(content, isOAuth, aliases))
81
102
  : value.content == null
82
103
  ? ""
83
104
  : [{ kind: "text" as const, text: String(value.content) }];
@@ -239,8 +260,9 @@ export function claudeResponseToEvents(
239
260
  model,
240
261
  },
241
262
  ];
263
+ const aliases = toolAliasesFor(request);
242
264
  const blocks = Array.isArray(json.content)
243
- ? json.content.map((content) => parseClaudeContent(content, isOAuth))
265
+ ? json.content.map((content) => parseClaudeContent(content, isOAuth, aliases))
244
266
  : [];
245
267
  let sequence = 2;
246
268
  for (const block of blocks) {
@@ -292,6 +314,8 @@ interface ClaudeStreamState {
292
314
  rawUsage: Record<string, unknown> | undefined;
293
315
  sawMessageStop: boolean;
294
316
  isOAuth: boolean;
317
+ /** Tool renames recorded for this request, for exact restore. */
318
+ aliases: ReadonlyArray<ToolNameAlias>;
295
319
  }
296
320
 
297
321
  function streamEventToCanonical(
@@ -321,7 +345,7 @@ function streamEventToCanonical(
321
345
  state.calls.set(index, {
322
346
  call_id: block.id,
323
347
  ...(stringValue(block.name)
324
- ? { name: unprefixClaudeToolName(block.name, state.isOAuth) }
348
+ ? { name: restoreClaudeToolName(block.name, state.isOAuth, state.aliases) }
325
349
  : {}),
326
350
  });
327
351
  // Anthropic sends `input: {}` as a *placeholder* on content_block_start,
@@ -332,13 +356,14 @@ function streamEventToCanonical(
332
356
  // send complete arguments here still gets them through.
333
357
  if (
334
358
  isRecord(block.input) &&
335
- Object.keys(block.input).length > 0
359
+ Object.keys(block.input).length > 0 &&
360
+ stringValue(block.name)
336
361
  ) {
337
362
  events.push({
338
363
  type: "tool_call_delta",
339
364
  sequence_number: next++,
340
365
  call_id: block.id,
341
- ...(stringValue(block.name) ? { name: unprefixClaudeToolName(block.name, state.isOAuth) } : {}),
366
+ name: restoreClaudeToolName(block.name, state.isOAuth, state.aliases),
342
367
  arguments_delta: JSON.stringify(block.input),
343
368
  });
344
369
  }
@@ -446,6 +471,7 @@ export async function* parseClaudeSseStream(
446
471
  body: ReadableStream<Uint8Array>,
447
472
  signal?: AbortSignal,
448
473
  isOAuth = false,
474
+ aliases: ReadonlyArray<ToolNameAlias> = [],
449
475
  ): AsyncIterable<CanonicalEvent> {
450
476
  const state: ClaudeStreamState = {
451
477
  calls: new Map(),
@@ -455,6 +481,7 @@ export async function* parseClaudeSseStream(
455
481
  rawUsage: undefined,
456
482
  sawMessageStop: false,
457
483
  isOAuth,
484
+ aliases,
458
485
  };
459
486
  let sequence = 1;
460
487
  for await (const sse of decodeSseEvents(body, { signal })) {