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
@@ -262,11 +262,30 @@ export function jsonCompletion(
262
262
  };
263
263
  const terminal = events.findLast((event) => event.type === "terminal");
264
264
  if (terminal?.type === "terminal" && terminal.state === "failed") {
265
- result.error = {
266
- type: "server_error",
267
- code: terminal.stop_reason ?? "provider_error",
268
- message: "The upstream response failed",
265
+ const raw = isRecord(terminal.stop_details) ? terminal.stop_details : undefined;
266
+ const errorMessage =
267
+ typeof raw?.["message"] === "string" && raw["message"].length > 0
268
+ ? raw["message"]
269
+ : "The upstream response failed";
270
+ // A failed terminal returns an error response, not a completion with a
271
+ // finish_reason: the client must be able to tell the refusal/limit from a
272
+ // finished answer. OpenAI error responses carry no `choices`.
273
+ const errorResult: JsonObject = {
274
+ id,
275
+ object: "chat.completion",
276
+ created,
277
+ model,
278
+ usage,
279
+ error: {
280
+ type: "server_error",
281
+ code: terminal.stop_reason ?? "provider_error",
282
+ ...(typeof raw?.["status"] === "number" ? { status: raw["status"] } : {}),
283
+ message: errorMessage,
284
+ },
269
285
  };
286
+ const serviceTier = options.service_tier ?? options.serviceTier;
287
+ if (serviceTier !== undefined) errorResult.service_tier = serviceTier;
288
+ return errorResult;
270
289
  }
271
290
  const serviceTier = options.service_tier ?? options.serviceTier;
272
291
  if (serviceTier !== undefined) result.service_tier = serviceTier;
@@ -482,6 +501,30 @@ export class ChatStreamEncoder extends SurfaceStreamEncoder<CanonicalEvent, Json
482
501
  if (event.type === "terminal") {
483
502
  this.terminalState = event.state;
484
503
  this.terminalReason = finishReason(event.stop_reason);
504
+ // A failed terminal is a real failure: emit an error frame with the
505
+ // upstream's message instead of a `finish_reason: "error"` chunk. The
506
+ // client receives the reason (rate limit, quota, upstream error) rather
507
+ // than a bare "error" word it has to guess at. `stop_details` carries
508
+ // the failure details when the adapter set them; otherwise the stop
509
+ // reason is the only signal.
510
+ if (event.state === "failed") {
511
+ const raw = isRecord(event.stop_details) ? event.stop_details : undefined;
512
+ const message =
513
+ typeof raw?.["message"] === "string" && raw["message"].length > 0
514
+ ? raw["message"]
515
+ : "The upstream response failed";
516
+ this.emittedAny = true;
517
+ return [
518
+ {
519
+ error: {
520
+ type: "server_error",
521
+ code: event.stop_reason ?? "provider_error",
522
+ ...(typeof raw?.["status"] === "number" ? { status: raw["status"] } : {}),
523
+ message,
524
+ },
525
+ },
526
+ ];
527
+ }
485
528
  if (!this.cancelled && event.state !== "aborted" && event.usage)
486
529
  this.finalUsage = usageToChatWire(event.usage);
487
530
  const usage = this.cancelled || event.state === "aborted" ? null : this.finalUsage;
@@ -2,7 +2,7 @@ import type { CanonicalMessage, ContentPart, GenerationControls } from "../../..
2
2
  import type { SurfaceInput } from "../adapters";
3
3
  import { isRecord } from "../../../protocol/codecs";
4
4
  import { asNumber } from "../../../shared/coerce";
5
- import { textPart, normalizeInlineFile } from "../content-parts";
5
+ import { decodeWireBody, textPart, normalizeInlineFile } from "../content-parts";
6
6
  import { SERVICE_TIERS } from "../dialects";
7
7
  import { readNumber, readString } from "../../../protocol/codecs";
8
8
  import type { OpenAiUsage } from "../../../providers/provider-usage";
@@ -61,18 +61,8 @@ function isSurfaceInput(value: unknown): value is SurfaceInput {
61
61
  return isRecord(value) && "body" in value && "path" in value && typeof value.path === "string";
62
62
  }
63
63
 
64
- export function decodeBody(value: unknown): unknown {
65
- if (value instanceof Uint8Array) {
66
- return JSON.parse(new TextDecoder().decode(value)) as unknown;
67
- }
68
- if (typeof value === "string") {
69
- return JSON.parse(value) as unknown;
70
- }
71
- return value;
72
- }
73
-
74
64
  export function requestBody(input: unknown): unknown {
75
- return decodeBody(isSurfaceInput(input) ? input.body : input);
65
+ return decodeWireBody(isSurfaceInput(input) ? input.body : input);
76
66
  }
77
67
 
78
68
  function nonNegativeNumber(value: unknown): number | undefined {
@@ -1,11 +1,12 @@
1
1
  import { GatewayError } from "../../protocol/gateway-error";
2
2
  import type { CanonicalEvent, CanonicalRequest, GenerationControls } from "../../protocol/canonical-model";
3
- import type { ReasoningIntent } from "../../protocol/canonical-model";
3
+ import { parseReasoningIntent } from "./dialects";
4
4
  import type { SurfaceAdapter, SurfaceInput, SurfaceOutput } from "./adapters";
5
5
  import { eventText } from "./adapters";
6
6
  import { isRecord } from "../../protocol/codecs";
7
7
  import { textPart } from "./content-parts";
8
8
  import { usageToChatWire, type OpenAiUsage } from "../../providers/provider-usage";
9
+ import { completionFinishReason } from "../../protocol/stop-reason";
9
10
  import { TEXT_ENCODER } from "../../protocol/sse-frame";
10
11
  import { SurfaceStreamEncoder } from "./stream-frame";
11
12
 
@@ -35,24 +36,24 @@ function textPrompts(value: unknown): string[] {
35
36
  return value.flatMap((prompt) => (typeof prompt === "string" ? [prompt] : []));
36
37
  }
37
38
 
38
- function stopReason(events: readonly CanonicalEvent[]): string | null {
39
- const terminal = [...events].reverse().find((event) => event.type === "terminal");
39
+ /**
40
+ * Completion's wire stop reason from a terminal event (or `null` when no
41
+ * terminal has been seen). Both the sync adapter and the streaming encoder
42
+ * derive it through the shared canonical stop-reason mapping so the two
43
+ * halves of the completion surface cannot drift.
44
+ */
45
+ function completionStopReason(terminal: CanonicalEvent | undefined): string | null {
40
46
  if (terminal?.type !== "terminal") return null;
41
- if (terminal.stop_reason === "length") return "length";
42
- if (terminal.stop_reason === "content_filter") return "content_filter";
43
- if (terminal.state === "complete" || terminal.stop_reason === "stop") return "stop";
44
- return null;
47
+ return completionFinishReason(terminal.stop_reason, terminal.state);
45
48
  }
46
49
 
47
50
  /**
48
51
  * Completion wire usage. Delegates to the chat builder (same wire family), so
49
52
  * completion responses carry cache/reasoning details, not just the three totals.
50
53
  */
51
- function usage(events: readonly CanonicalEvent[]): OpenAiUsage | undefined {
52
- const terminal = [...events].reverse().find((event) => event.type === "terminal");
54
+ function completionUsage(terminal: CanonicalEvent | undefined, usageEvent: CanonicalEvent | undefined): OpenAiUsage | undefined {
53
55
  const record = terminal?.type === "terminal" ? terminal.usage : undefined;
54
- const event = [...events].reverse().find((item) => item.type === "usage");
55
- const source = record ?? (event?.type === "usage" ? event.usage : undefined);
56
+ const source = record ?? (usageEvent?.type === "usage" ? usageEvent.usage : undefined);
56
57
  if (!source) return undefined;
57
58
  return usageToChatWire(source);
58
59
  }
@@ -103,6 +104,31 @@ export class CompletionStreamEncoder extends SurfaceStreamEncoder<CanonicalEvent
103
104
  push(event: CanonicalEvent): Record<string, unknown>[] {
104
105
  if (event.type === "terminal") this.terminalEvent = event;
105
106
  if (event.type === "usage") this.usageEvent = event;
107
+ if (event.type === "terminal" && event.state === "failed") {
108
+ // A failed terminal is a real failure: emit an error object instead of
109
+ // a completion with a null finish_reason. The client receives the
110
+ // upstream's message (rate limit, quota, error) rather than a silent
111
+ // empty completion.
112
+ const raw = isRecord(event.stop_details) ? event.stop_details : undefined;
113
+ const message =
114
+ typeof raw?.["message"] === "string" && raw["message"].length > 0
115
+ ? raw["message"]
116
+ : "The upstream response failed";
117
+ return [
118
+ {
119
+ id: this.id,
120
+ object: "text_completion",
121
+ created: this.created,
122
+ model: this.options.model ?? "",
123
+ error: {
124
+ type: "server_error",
125
+ code: event.stop_reason ?? "provider_error",
126
+ ...(typeof raw?.["status"] === "number" ? { status: raw["status"] } : {}),
127
+ message,
128
+ },
129
+ },
130
+ ];
131
+ }
106
132
  const text = eventText(event);
107
133
  if (text === undefined) {
108
134
  if (event.type !== "terminal") return [];
@@ -116,22 +142,12 @@ export class CompletionStreamEncoder extends SurfaceStreamEncoder<CanonicalEvent
116
142
 
117
143
  /** The wire stop reason from the terminal event seen so far. */
118
144
  private stopReason(): string | null {
119
- const terminal = this.terminalEvent;
120
- if (terminal?.type !== "terminal") return null;
121
- if (terminal.stop_reason === "length") return "length";
122
- if (terminal.stop_reason === "content_filter") return "content_filter";
123
- if (terminal.state === "complete" || terminal.stop_reason === "stop") return "stop";
124
- return null;
145
+ return completionStopReason(this.terminalEvent);
125
146
  }
126
147
 
127
148
  /** The wire usage from the terminal event, else the last usage event. */
128
149
  private usage(): OpenAiUsage | undefined {
129
- const terminal = this.terminalEvent;
130
- const record = terminal?.type === "terminal" ? terminal.usage : undefined;
131
- const event = this.usageEvent;
132
- const source = record ?? (event?.type === "usage" ? event.usage : undefined);
133
- if (!source) return undefined;
134
- return usageToChatWire(source);
150
+ return completionUsage(this.terminalEvent, this.usageEvent);
135
151
  }
136
152
 
137
153
  finish(): Record<string, unknown>[] {
@@ -180,14 +196,7 @@ class CompletionAdapter implements SurfaceAdapter {
180
196
  if (typeof value.logprobs === "boolean") controls.logprobs = value.logprobs;
181
197
  if (typeof value.stop === "string" || (Array.isArray(value.stop) && value.stop.every((item) => typeof item === "string")))
182
198
  controls.stop = value.stop as string | readonly string[];
183
- const requestedReasoning = value.reasoning_effort ?? value.reasoning_level;
184
- const reasoningEfforts = new Set<NonNullable<ReasoningIntent["effort"]>>([
185
- "none", "minimal", "low", "medium", "high", "xhigh", "max",
186
- ]);
187
- const reasoning =
188
- typeof requestedReasoning === "string" && reasoningEfforts.has(requestedReasoning as NonNullable<ReasoningIntent["effort"]>)
189
- ? { effort: requestedReasoning as NonNullable<ReasoningIntent["effort"]> }
190
- : undefined;
199
+ const reasoning = parseReasoningIntent(value);
191
200
  const cacheKey = value.prompt_cache_key;
192
201
  const cacheHint =
193
202
  (typeof cacheKey === "string" && cacheKey.length > 0) || value.cache_control === "auto"
@@ -211,8 +220,10 @@ class CompletionAdapter implements SurfaceAdapter {
211
220
 
212
221
  encodeOutput(events: readonly CanonicalEvent[], options: CompletionEncodingOptions = {}): SurfaceOutput {
213
222
  const model = modelFor(events, options);
214
- const reason = stopReason(events);
215
- const responseUsage = usage(events);
223
+ const terminal = [...events].reverse().find((event) => event.type === "terminal");
224
+ const usageEvent = [...events].reverse().find((event) => event.type === "usage");
225
+ const reason = completionStopReason(terminal);
226
+ const responseUsage = completionUsage(terminal, usageEvent);
216
227
  const response = {
217
228
  id: options.response_id ?? `cmpl-${crypto.randomUUID()}`,
218
229
  object: "text_completion",
@@ -6,6 +6,27 @@ export function textPart(text: string): ContentPart {
6
6
  return { kind: "text", text };
7
7
  }
8
8
 
9
+ /**
10
+ * Decodes a wire body value into its parsed JSON form.
11
+ *
12
+ * Every surface parser receives bodies in the same three shapes — raw bytes,
13
+ * an already-decoded string, or an object handed in directly (surface
14
+ * detection and tests pass objects without a wire round-trip). Bytes and
15
+ * strings are parsed as JSON; anything else passes through unchanged. The
16
+ * `SyntaxError` thrown on malformed JSON propagates to the same
17
+ * error-normalization path on every surface, so one shared decoder keeps the
18
+ * four surfaces' wire-read convention identical.
19
+ */
20
+ export function decodeWireBody(value: unknown): unknown {
21
+ if (value instanceof Uint8Array) {
22
+ return JSON.parse(new TextDecoder().decode(value)) as unknown;
23
+ }
24
+ if (typeof value === "string") {
25
+ return JSON.parse(value) as unknown;
26
+ }
27
+ return value;
28
+ }
29
+
9
30
  /**
10
31
  * Fields a raw file/document block may carry, normalized across every origin
11
32
  * vocabulary (OpenAI Chat `file`, OpenAI Responses `input_file`, Anthropic
@@ -153,10 +153,16 @@ export class MessagesAdapter implements SurfaceAdapter {
153
153
  : data.reason;
154
154
  const usage = usageToMessagesWire(data.usage);
155
155
  if (data.failed) {
156
+ const errorEvent = [...events]
157
+ .reverse()
158
+ .find((event): event is Extract<CanonicalEvent, { type: "error" }> => event.type === "error");
156
159
  return {
157
160
  id: responseId,
158
161
  type: "error",
159
- error: { type: "server_error", message: "The upstream response failed" },
162
+ error: {
163
+ type: "server_error",
164
+ message: errorEvent?.message ?? "The upstream response failed",
165
+ },
160
166
  };
161
167
  }
162
168
  return {
@@ -207,11 +207,14 @@ export function groupedBlocks(events: readonly CanonicalEvent[]): {
207
207
  previousReasoningText,
208
208
  nextText,
209
209
  );
210
- if (reasoningSummaryHasBoundary(previousReasoningIdentity, identity)) {
210
+ // A thinking block splits only on a strong boundary (a real paragraph
211
+ // break). Copilot streams one thought as many items; treating every
212
+ // identity change as a new block painted dozens of tiny thinking blocks.
213
+ if (reasoningSummaryHasBoundary(previousReasoningIdentity, identity) && separator === "\n\n") {
211
214
  next.thinking = `${separator}${nextText}`;
212
215
  blocks.push(next);
213
216
  } else {
214
- previous.thinking = `${String(previous.thinking ?? "")}${nextText}`;
217
+ previous.thinking = `${String(previous.thinking ?? "")}${separator}${nextText}`;
215
218
  }
216
219
  previousReasoningIdentity = identity;
217
220
  previousReasoningText = nextText;
@@ -264,7 +267,12 @@ export function stopData(events: readonly CanonicalEvent[]): {
264
267
  const usageEvent = [...events]
265
268
  .reverse()
266
269
  .find((event): event is Extract<CanonicalEvent, { type: "usage" }> => event.type === "usage");
267
- const failed = terminal?.state === "failed";
270
+ // A failed terminal — `failed`, or `aborted` (upstream stream error) —
271
+ // marks the response as failed. An explicit canonical `error` event before
272
+ // the terminal also fails it: the upstream declared a failure even when the
273
+ // terminal state alone does not.
274
+ const hasErrorEvent = events.some((event) => event.type === "error");
275
+ const failed = terminal?.state === "failed" || (terminal?.state === "aborted" && hasErrorEvent);
268
276
  let reason: string | null;
269
277
  if (terminal?.stop_reason !== undefined) reason = mapStopReason(terminal.stop_reason);
270
278
  else reason = terminal?.state === "complete" ? "end_turn" : null;
@@ -50,7 +50,8 @@ export interface MessagesStreamEvent extends MessagesWireObject {
50
50
  | "content_block_delta"
51
51
  | "content_block_stop"
52
52
  | "message_delta"
53
- | "message_stop";
53
+ | "message_stop"
54
+ | "error";
54
55
  }
55
56
 
56
57
  function contentArray(value: unknown, label: string): unknown[] {
@@ -1,6 +1,5 @@
1
1
  import type { CanonicalEvent, UsageRecord } from "../../../protocol/canonical-model";
2
2
  import {
3
- reasoningSummaryHasBoundary,
4
3
  reasoningSummaryIdentity,
5
4
  reasoningSummarySeparator,
6
5
  type ReasoningSummaryIdentity,
@@ -62,6 +61,7 @@ export class MessagesStreamEncoder extends SurfaceStreamEncoder<CanonicalEvent,
62
61
  private droppedUnnamedToolCalls = 0;
63
62
  private usage: UsageRecord | undefined;
64
63
  private failed = false;
64
+ private errorMessage: string | undefined;
65
65
  private stopReason: string | null = null;
66
66
  private readonly responseId: string;
67
67
  private readonly model: string;
@@ -199,6 +199,7 @@ export class MessagesStreamEncoder extends SurfaceStreamEncoder<CanonicalEvent,
199
199
  // inevitable terminal push throw, producing a second error frame
200
200
  // after message_stop (which Anthropic never sends).
201
201
  this.failed = true;
202
+ if (this.errorMessage === undefined) this.errorMessage = event.message;
202
203
  return out;
203
204
  }
204
205
  if (event.type === "content_delta") {
@@ -247,11 +248,7 @@ export class MessagesStreamEncoder extends SurfaceStreamEncoder<CanonicalEvent,
247
248
  summary,
248
249
  )
249
250
  : "";
250
- if (
251
- blockType === "thinking" &&
252
- reasoningSummaryHasBoundary(this.openReasoningIdentity, identity) &&
253
- this.openBlock?.type === blockType
254
- ) {
251
+ if (blockType === "thinking" && separator === "\n\n" && this.openBlock?.type === blockType) {
255
252
  out.push({ type: "content_block_stop", index: this.openBlock.index });
256
253
  this.openBlock = undefined;
257
254
  }
@@ -401,18 +398,25 @@ export class MessagesStreamEncoder extends SurfaceStreamEncoder<CanonicalEvent,
401
398
  resetToolCallTracker(this.tools);
402
399
  const finalUsage = usageToMessagesWire(this.usage);
403
400
  if (this.failed) {
404
- out.push({
405
- type: "message_delta",
406
- delta: { stop_reason: null, stop_sequence: null },
407
- usage: finalUsage,
408
- });
409
- } else {
410
- out.push({
411
- type: "message_delta",
412
- delta: { stop_reason: this.stopReason, stop_sequence: null },
413
- usage: finalUsage,
414
- });
401
+ // The stream is closing on an upstream failure, not a normal end. A
402
+ // `message_stop` here would hide the failure from the client (it
403
+ // reads a clean stop), so emit the error event instead — matching the
404
+ // semantic the non-streaming encoder uses for `failed` terminals.
405
+ return [
406
+ {
407
+ type: "error",
408
+ error: {
409
+ type: "server_error",
410
+ message: this.errorMessage ?? "The upstream response failed",
411
+ },
412
+ },
413
+ ];
415
414
  }
415
+ out.push({
416
+ type: "message_delta",
417
+ delta: { stop_reason: this.stopReason, stop_sequence: null },
418
+ usage: finalUsage,
419
+ });
416
420
  out.push({ type: "message_stop" });
417
421
  this.finished = true;
418
422
  return out;
@@ -446,6 +450,18 @@ export class MessagesStreamEncoder extends SurfaceStreamEncoder<CanonicalEvent,
446
450
  ];
447
451
  }
448
452
  if (this.finished) return [];
453
+ if (this.failed) {
454
+ this.finished = true;
455
+ return [
456
+ {
457
+ type: "error",
458
+ error: {
459
+ type: "server_error",
460
+ message: this.errorMessage ?? "The upstream response failed",
461
+ },
462
+ },
463
+ ];
464
+ }
449
465
  const out: MessagesStreamEvent[] = [];
450
466
  out.push(...this.closeOpenBlock());
451
467
  out.push(...this.flushToolCalls(true));
@@ -54,6 +54,7 @@ export class ResponsesEventEncoder extends SurfaceStreamEncoder<CanonicalEvent,
54
54
  private inputSequence = -Infinity;
55
55
  private started = false;
56
56
  private terminal = false;
57
+ private hasError = false;
57
58
  private responseModel: string | undefined;
58
59
  private systemFingerprint: string | undefined;
59
60
  private usage: UsageRecord | undefined;
@@ -624,21 +625,33 @@ export class ResponsesEventEncoder extends SurfaceStreamEncoder<CanonicalEvent,
624
625
  state: "complete" | "failed" | "aborted",
625
626
  stopReason?: CanonicalStopReason,
626
627
  usage?: UsageRecord,
628
+ stopDetails?: unknown,
627
629
  ): ResponsesWireEvent[] {
628
630
  if (this.terminal)
629
631
  throw new ResponsesLifecycleError("Responses stream has more than one terminal event");
630
632
  if (usage !== undefined) this.usage = usage;
631
633
  const events = this.closeAll();
632
- const status = terminalResponseStatus(state);
634
+ // An upstream error event before an `aborted` terminal is a failure, not
635
+ // an incomplete response: the client must see `response.failed` rather
636
+ // than `response.incomplete` (the same distinction the Messages encoder
637
+ // applies in `stopData`).
638
+ const effectiveState = state === "aborted" && this.hasError ? "failed" : state;
639
+ const status = terminalResponseStatus(effectiveState);
633
640
  const response = this.responseObject(status.status);
634
641
  const incompleteReason =
635
642
  status.status === "incomplete" ? incompleteResponseReason(stopReason) : undefined;
636
643
  if (incompleteReason !== undefined) response.incomplete_details = { reason: incompleteReason };
637
- if (state === "failed") {
644
+ if (effectiveState === "failed") {
645
+ const raw = isRecord(stopDetails) ? stopDetails : undefined;
646
+ const message =
647
+ typeof raw?.["message"] === "string" && raw["message"].length > 0
648
+ ? raw["message"]
649
+ : "The upstream response failed";
638
650
  response.error = {
639
651
  type: "server_error",
640
652
  code: stopReason ?? "provider_error",
641
- message: "The upstream response failed",
653
+ ...(typeof raw?.["status"] === "number" ? { status: raw["status"] } : {}),
654
+ message,
642
655
  };
643
656
  }
644
657
  events.push(this.emit(status.event, { response }));
@@ -699,12 +712,16 @@ export class ResponsesEventEncoder extends SurfaceStreamEncoder<CanonicalEvent,
699
712
  return events;
700
713
  }
701
714
  if (event.type === "keepalive") return events.concat(this.emit("response.keepalive"));
702
- if (event.type === "error")
715
+ if (event.type === "error") {
716
+ this.hasError = true;
703
717
  return events.concat(
704
718
  this.emit("response.error", { category: event.category, message: event.message }),
705
719
  );
720
+ }
706
721
  if (event.type === "terminal")
707
- return events.concat(this.processTerminal(event.state, event.stop_reason, event.usage));
722
+ return events.concat(
723
+ this.processTerminal(event.state, event.stop_reason, event.usage, event.stop_details),
724
+ );
708
725
  throw new ResponsesLifecycleError(`Unsupported canonical event type: ${event.type}`);
709
726
  }
710
727
 
@@ -1,6 +1,6 @@
1
1
  import type { CanonicalMessage, CanonicalRequest, ContentPart, GenerationControls, ToolDefinition } from "../../../protocol/canonical-model";
2
2
  import { isRecord } from "../../../protocol/codecs";
3
- import { textPart, normalizeInlineFile } from "../content-parts";
3
+ import { decodeWireBody, textPart, normalizeInlineFile } from "../content-parts";
4
4
  import {
5
5
  isServiceTier,
6
6
  parseReasoningIntent,
@@ -66,23 +66,15 @@ export function hasOwn(value: Record<string, unknown>, key: string): boolean {
66
66
  return Object.hasOwn(value, key);
67
67
  }
68
68
 
69
- function parseJsonBody(value: unknown): unknown {
70
- if (value instanceof Uint8Array) {
71
- return JSON.parse(new TextDecoder().decode(value)) as unknown;
72
- }
73
- if (typeof value === "string") return JSON.parse(value) as unknown;
74
- return value;
75
- }
76
-
77
69
  export function unwrapBody(input: unknown): unknown {
78
70
  if (
79
71
  isRecord(input) &&
80
72
  hasOwn(input, "body") &&
81
73
  (hasOwn(input, "headers") || hasOwn(input, "path"))
82
74
  ) {
83
- return parseJsonBody(input.body);
75
+ return decodeWireBody(input.body);
84
76
  }
85
- return parseJsonBody(input);
77
+ return decodeWireBody(input);
86
78
  }
87
79
 
88
80
  /**
@@ -29,7 +29,7 @@ import { sleep } from "../shared/sleep";
29
29
  import type {
30
30
  BuddyGrowthCall,
31
31
  BuddyGrowthResult,
32
- } from "../providers/integrations/buddy/buddy-growth-shared";
32
+ } from "../providers/integrations/buddy/lifecycle";
33
33
  import { withDirectFallback } from "./daily-checkin";
34
34
  import { providerBaseUrl, BUDDY_PROVIDER_IDS } from "../providers/provider-metadata";
35
35
  import type { FetchLike } from "../providers/quota/quota-contracts";
@@ -185,7 +185,7 @@ async function forAccount(
185
185
  // The buddy-oauth-shared graph is lazy-loaded: the uid decoder runs once per
186
186
  // pass, and only for accounts whose credential carries a JWT (B-02).
187
187
  const { buddyAccountUid } = await import(
188
- "../providers/integrations/buddy/buddy-oauth-shared"
188
+ "../providers/integrations/buddy/account"
189
189
  );
190
190
  const call: BuddyGrowthCall = {
191
191
  baseUrl: providerBaseUrl(account.providerId) ?? "",
@@ -264,7 +264,7 @@ export async function buddyStreakTask(deps: BuddyTaskDeps) {
264
264
  // The buddy-growth-shared graph is lazy-loaded: this task only runs once a
265
265
  // day, and must not pull the integration into the boot graph (B-02).
266
266
  const { useBuddyMakeupCard } = await import(
267
- "../providers/integrations/buddy/buddy-growth-shared"
267
+ "../providers/integrations/buddy/lifecycle"
268
268
  );
269
269
  return runTask(deps, "buddy-streak", (call) => useBuddyMakeupCard(call));
270
270
  }
@@ -283,7 +283,7 @@ export async function buddyAdoptTask(deps: BuddyTaskDeps) {
283
283
  agreeBuddyAgreement,
284
284
  readBuddyFirstTask,
285
285
  readBuddyInfo,
286
- } = await import("../providers/integrations/buddy/buddy-growth-shared");
286
+ } = await import("../providers/integrations/buddy/lifecycle");
287
287
  return runTask(deps, "buddy-adopt", async (call) => {
288
288
  const info = await readBuddyInfo(call);
289
289
  if (info === null || info.hasBuddy) return null;
@@ -312,7 +312,7 @@ export async function buddyActivityReportTask(deps: BuddyTaskDeps) {
312
312
  // The buddy-checkin graph is lazy-loaded: this task only runs once a day,
313
313
  // and must not pull the integration into the boot graph (B-02).
314
314
  const { fetchBuddyActivityReport } = await import(
315
- "../providers/integrations/buddy/buddy-checkin"
315
+ "../providers/integrations/buddy/lifecycle"
316
316
  );
317
317
  return runTask(deps, "buddy-activity-report", async (call) => {
318
318
  if (call.uid === undefined) {
@@ -352,13 +352,13 @@ export async function syncBuddyUsage(deps: BuddyTaskDeps): Promise<number> {
352
352
  // six-hourly cadence, and must not pull the integrations into the boot
353
353
  // graph (B-02).
354
354
  const { fetchBuddyRequestUsage } = await import(
355
- "../providers/integrations/buddy/buddy-usage-shared"
355
+ "../providers/integrations/buddy/account"
356
356
  );
357
357
  const { saveBuddyUsageSnapshots } = await import(
358
- "../providers/integrations/buddy/buddy-usage-store"
358
+ "../providers/integrations/buddy/account"
359
359
  );
360
360
  const { buddyAccountUid } = await import(
361
- "../providers/integrations/buddy/buddy-oauth-shared"
361
+ "../providers/integrations/buddy/account"
362
362
  );
363
363
  const startedAt = Date.now();
364
364
  const budgetMs = deps.budgetMs;
@@ -20,14 +20,14 @@
20
20
  //
21
21
 
22
22
  import { and, eq, inArray } from "drizzle-orm";
23
- import type { ValidatedFetch } from "../network/outbound-fetch";
24
- import type { ValidatedNetworkBindingFactory } from "../network/pool/resolver";
23
+ import type { ValidatedFetch } from "../network/egress/fetch";
24
+ import type { ValidatedNetworkBindingFactory } from "../network/egress/factory";
25
25
  import type { RedisClient } from "../persistence/redis";
26
26
  import type { CartethyiaDatabase } from "../persistence/db-handle";
27
27
  import { dailyCheckinLedger, networkPools } from "../persistence/schema";
28
28
  import type { ProviderId } from "../providers/provider-contracts";
29
29
  import { log } from "../observability/logger";
30
- import type { DailyCheckinResult } from "../providers/integrations/buddy/buddy-checkin";
30
+ import type { DailyCheckinResult } from "../providers/integrations/buddy/lifecycle";
31
31
  import { BUDDY_PROVIDER_IDS } from "../providers/provider-metadata";
32
32
 
33
33
  export type { DailyCheckinResult };
@@ -486,7 +486,7 @@ export async function attemptDailyGrowthPass(args: {
486
486
  });
487
487
  } else {
488
488
  const { fetchDailyCheckin } = await import(
489
- "../providers/integrations/buddy/buddy-checkin"
489
+ "../providers/integrations/buddy/lifecycle"
490
490
  );
491
491
  checkin = await fetchDailyCheckin({
492
492
  providerId,
@@ -26,7 +26,7 @@ import {
26
26
  type CachedQuotaEntry,
27
27
  } from "../providers/quota/cache";
28
28
  import { runSweep } from "./sweep-runner";
29
- import type { ValidatedFetch } from "../network/outbound-fetch";
29
+ import type { ValidatedFetch } from "../network/egress/fetch";
30
30
  import type { ProviderId } from "../providers/provider-contracts";
31
31
  import {
32
32
  attemptDailyGrowthPass,
@@ -5,7 +5,7 @@ import type { CartethyiaDatabase } from "../persistence/db-handle";
5
5
  import type { RedisClient } from "../persistence/redis";
6
6
  import { providerAccounts } from "../persistence/schema";
7
7
  import type { ProviderId } from "../providers/provider-contracts";
8
- import type { ValidatedFetch } from "../network/outbound-fetch";
8
+ import type { ValidatedFetch } from "../network/egress/fetch";
9
9
  import { invalidateQuotaCacheForAccount } from "../providers/quota/cache";
10
10
  import { claimZcodeGifts } from "../providers/integrations/zcode-start-plan/gifts";
11
11
  import { ZCODE_START_PLAN_PROVIDER_ID } from "../providers/integrations/zcode-start-plan/provider";