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
@@ -2,7 +2,7 @@ import { withBearerAuthentication, type OpenAICompatibleAdapterConfig } from "..
2
2
  import type { CanonicalRequest } from "../../../../protocol/canonical-model";
3
3
  import type { ProviderDispatchContext, ProviderDispatchTarget, ProviderId } from "../../../provider-contracts";
4
4
  import { resolveInboundSessionId } from "../../../operations/session-resolution";
5
- import { buddyConversationHeaders } from "../buddy-conversation-headers";
5
+ import { buddyConversationHeaders } from "../headers";
6
6
  import {
7
7
  buddyAgentPurpose,
8
8
  buddyEnterprise,
@@ -11,10 +11,10 @@ import {
11
11
  buddyUserId,
12
12
  resolveBuddyDeviceBinding,
13
13
  resolveBuddyIdentity,
14
- } from "../buddy-identity";
14
+ } from "../headers";
15
15
  import { buildCodeBuddyUserAgent, getCodeBuddyCnVersion, getCodeBuddyVersion } from "../../../operations/client-versions";
16
- import { buddyDeviceTokenHeaders } from "../buddy-identity";
17
- import { buddyCliHeaders, buddyCliTraceHeaders } from "../buddy-cli-headers";
16
+ import { buddyDeviceTokenHeaders } from "../headers";
17
+ import { buddyCliHeaders, buddyCliTraceHeaders } from "../headers";
18
18
 
19
19
  /** Client identity variant behind the per-request CodeBuddy headers. */
20
20
  export type CodeBuddyVariant = "IDE" | "CLI";
@@ -87,7 +87,7 @@ export async function codebuddyHeaders(
87
87
  };
88
88
  }
89
89
  const purpose = buddyAgentPurpose(context, request);
90
- return {
90
+ const headers: Record<string, string> = {
91
91
  accept: "text/event-stream",
92
92
  "User-Agent": ua,
93
93
  "X-Product": "SaaS",
@@ -100,28 +100,29 @@ export async function codebuddyHeaders(
100
100
  "X-Domain": codebuddyDomain(variant),
101
101
  "x-requested-with": "XMLHttpRequest",
102
102
  "x-codebuddy-request": "1",
103
- // The effective agent purpose, matching the reference's dynamic value.
104
- // The reference does not invent `conversation`; it sends the purpose only
105
- // when one exists.
106
- ...(purpose === undefined ? {} : { "X-Agent-Purpose": purpose }),
107
- // The upstream's channel check reads the turn-level aggregation key, and a
108
- // chat request without it is refused for a missing conversation id.
109
- ...conversation,
110
- // Per-account device binding (qimei36) — distinct device per account.
111
- ...(() => {
112
- const binding = resolveBuddyDeviceBinding(context);
113
- return binding === undefined ? {} : { "X-Device-Id-List": JSON.stringify({ qimei36: binding }) };
114
- })(),
115
- // The desktop client sends the device-token pair on both realms. Captured
116
- // token (auth_state / device-token file) when present, otherwise the
117
- // official failure placeholder. (CLI branch above already returned without
118
- // device headers.)
119
- ...(await buddyDeviceTokenHeaders({
120
- realm: identity === "IDE" ? "intl" : "cn",
121
- context,
122
- cliIdentity,
123
- })),
124
103
  };
104
+ // The effective agent purpose, matching the reference's dynamic value.
105
+ // The reference does not invent `conversation`; it sends the purpose only
106
+ // when one exists.
107
+ if (purpose !== undefined) headers["X-Agent-Purpose"] = purpose;
108
+ // The upstream's channel check reads the turn-level aggregation key, and a
109
+ // chat request without it is refused for a missing conversation id.
110
+ Object.assign(headers, conversation);
111
+ // Per-account device binding (qimei36) — distinct device per account.
112
+ const binding = resolveBuddyDeviceBinding(context);
113
+ if (binding !== undefined) {
114
+ headers["X-Device-Id-List"] = JSON.stringify({ qimei36: binding });
115
+ }
116
+ // The desktop client sends the device-token pair on both realms. Captured
117
+ // token (auth_state / device-token file) when present, otherwise the
118
+ // official failure placeholder. (CLI branch above already returned without
119
+ // device headers.)
120
+ Object.assign(headers, await buddyDeviceTokenHeaders({
121
+ realm: identity === "IDE" ? "intl" : "cn",
122
+ context,
123
+ cliIdentity,
124
+ }));
125
+ return headers;
125
126
  }
126
127
 
127
128
  /**
@@ -140,6 +141,14 @@ export function codebuddyAdapterConfig(args: {
140
141
  ) => void;
141
142
  fetchImpl?: typeof fetch;
142
143
  cliIdentity?: boolean;
144
+ /**
145
+ * Body compression for chat dispatches. The CodeBuddy CLI contract (donor
146
+ * `codebuddy2api`) gzips the JSON body for the CLI profiles and sends
147
+ * `Accept-Encoding: identity`. `"json"` (default) keeps plain JSON.
148
+ */
149
+ requestCompression?: "json" | "gzip";
150
+ /** When set, retry a channel-check 400 against this path (console fallback). */
151
+ channelFallbackPath?: string;
143
152
  }): OpenAICompatibleAdapterConfig {
144
153
  return withBearerAuthentication({
145
154
  provider_id: args.providerId,
@@ -147,6 +156,12 @@ export function codebuddyAdapterConfig(args: {
147
156
  buildExtraHeaders: (context, request) =>
148
157
  codebuddyHeaders(args.variant, context, request, args.cliIdentity),
149
158
  prePayload: args.prePayload,
159
+ ...(args.requestCompression === undefined
160
+ ? {}
161
+ : { request_compression: args.requestCompression }),
162
+ ...(args.channelFallbackPath === undefined
163
+ ? {}
164
+ : { channel_fallback_path: args.channelFallbackPath }),
150
165
  ...(args.fetchImpl ? { fetchImpl: args.fetchImpl } : {}),
151
166
  });
152
167
  }
@@ -0,0 +1,311 @@
1
+ import type { ProviderDispatchContext } from "../../provider-contracts";
2
+ import { createHash } from "node:crypto";
3
+ import { readFileSync } from "node:fs";
4
+ import { decodeJwtPayload } from "../../authentication/oauth-flow-store";
5
+ import { buddyAccountUid } from "./account";
6
+ import type { CanonicalRequest } from "../../../protocol/canonical-model";
7
+
8
+ // ── buddy-identity ──────────────────────────────────────────
9
+
10
+ export type BuddyIdentity = "desktop" | "cli";
11
+
12
+ export function resolveBuddyIdentity(
13
+ context: ProviderDispatchContext | undefined,
14
+ profileIdentity?: boolean | undefined,
15
+ ): BuddyIdentity {
16
+ const accountRaw = context?.credential.auth_state?.["cliIdentity"];
17
+ if (accountRaw === true) return "cli";
18
+ if (accountRaw === false) return "desktop";
19
+ return profileIdentity === true ? "cli" : "desktop";
20
+ }
21
+
22
+ export function buddyUserId(context?: ProviderDispatchContext): string {
23
+ const secret = context?.credential?.secret;
24
+ const token = secret ? new TextDecoder().decode(secret).trim() : "";
25
+ const uid = buddyAccountUid(token);
26
+ if (uid !== undefined) return uid;
27
+ const accountId = context?.credential.account_id;
28
+ return accountId !== undefined && accountId.length > 0 ? accountId : "anonymous";
29
+ }
30
+
31
+ const ENTERPRISE_CLAIMS: readonly string[] = [
32
+ "enterprise_id",
33
+ "enterpriseId",
34
+ "enterprise",
35
+ "tenant_id",
36
+ "tenantId",
37
+ "org_id",
38
+ "organization_id",
39
+ ];
40
+
41
+ export function buddyEnterprise(context?: ProviderDispatchContext): string | undefined {
42
+ const stored = context?.credential.auth_state?.["enterpriseId"];
43
+ if (typeof stored === "string" && stored.trim().length > 0) return stored.trim();
44
+ const token = context?.credential.secret;
45
+ if (token === undefined || token.length === 0) return undefined;
46
+ const claims = decodeJwtPayload(new TextDecoder().decode(token).trim());
47
+ if (claims === undefined) return undefined;
48
+ for (const claim of ENTERPRISE_CLAIMS) {
49
+ const value = claims[claim];
50
+ if (typeof value === "string" && value.trim().length > 0) return value.trim();
51
+ }
52
+ return undefined;
53
+ }
54
+
55
+ export function resolveBuddyDeviceBinding(context?: ProviderDispatchContext): string | undefined {
56
+ const explicit = context?.credential.auth_state?.["qimei36"];
57
+ if (typeof explicit === "string" && explicit.trim().length > 0) return explicit.trim();
58
+ const accountId = context?.credential.account_id;
59
+ if (accountId === undefined || accountId.length === 0) return undefined;
60
+ return createHash("sha256").update(`wb2a:qimei36:${accountId}`).digest("hex").slice(0, 32);
61
+ }
62
+
63
+ export function materializeBuddyDeviceBinding(
64
+ accountId: string,
65
+ authState: Readonly<Record<string, unknown>> | undefined,
66
+ ): Record<string, unknown> | undefined {
67
+ const existing = authState?.["qimei36"];
68
+ if (typeof existing === "string" && existing.trim().length > 0) return undefined;
69
+ if (accountId.length === 0) return undefined;
70
+ const derived = createHash("sha256").update(`wb2a:qimei36:${accountId}`).digest("hex").slice(0, 32);
71
+ return { ...(authState ?? {}), qimei36: derived };
72
+ }
73
+
74
+ function identityClaim(claims: Record<string, unknown> | undefined, name: string): string | undefined {
75
+ if (claims === undefined) return undefined;
76
+ const value = claims[name];
77
+ if (typeof value === "string" && value.trim().length > 0 && value.trim().length <= 128) return value.trim();
78
+ return undefined;
79
+ }
80
+
81
+ export function buddyOAuthIdentityHeaders(
82
+ context?: ProviderDispatchContext,
83
+ ): Record<string, string> {
84
+ const secret = context?.credential?.secret;
85
+ const token = secret ? new TextDecoder().decode(secret).trim() : "";
86
+ const uid = buddyAccountUid(token) ?? context?.credential.account_id ?? "";
87
+ if (uid.length === 0) return {};
88
+ const enterpriseId = buddyEnterprise(context);
89
+ // The reference gateway reads `id_source` / `token_source` from the JWT
90
+ // payload and the account record, not from stored auth state — an account
91
+ // authorized before either was persisted still carries them on the wire.
92
+ // Precedence matches the donor (`client_profiles._identity_claim_headers`):
93
+ // account fields first, then JWT claims, then legacy `auth_state`.
94
+ const claims = token.length > 0 ? decodeJwtPayload(token) : undefined;
95
+ const state = context?.credential.auth_state;
96
+ const idSource =
97
+ identityClaim(claims, "id_source") ??
98
+ identityClaim(claims, "idSource") ??
99
+ identityClaim(state as Record<string, unknown> | undefined, "idSource");
100
+ const method =
101
+ identityClaim(claims, "token_source") ??
102
+ identityClaim(state as Record<string, unknown> | undefined, "authMethod");
103
+ const headers: Record<string, string> = { "X-User-Id": uid };
104
+ if (method !== undefined) headers["X-Auth-Method"] = method;
105
+ if (idSource !== undefined) headers["X-Id-Source"] = idSource;
106
+ if (enterpriseId !== undefined || idSource !== undefined || method !== undefined) {
107
+ const info: Record<string, string> = { uin: uid };
108
+ if (enterpriseId !== undefined) info["owner_uin"] = enterpriseId;
109
+ if (idSource !== undefined) info["id_source"] = idSource;
110
+ if (method !== undefined) info["token_source"] = method;
111
+ headers["X-Userinfo"] = Buffer.from(JSON.stringify(info), "utf-8").toString("base64");
112
+ }
113
+ return headers;
114
+ }
115
+
116
+ export function buddyPrivateData(context?: ProviderDispatchContext): boolean {
117
+ return context?.credential.auth_state?.["privateData"] === true;
118
+ }
119
+
120
+ export function buddyAgentPurpose(
121
+ context?: ProviderDispatchContext,
122
+ request?: CanonicalRequest,
123
+ ): string | undefined {
124
+ if (process.env.PERSONAL_AGENT_ROLE && process.env.PERSONAL_AGENT_ROLE.trim().length > 0) {
125
+ return "person_agent";
126
+ }
127
+ const inbound = context?.request_headers?.["x-agent-purpose"]?.trim();
128
+ if (inbound !== undefined && inbound.length > 0) return inbound;
129
+ const fromRequest = request?.metadata?.["agent_purpose"];
130
+ if (typeof fromRequest === "string" && fromRequest.trim().length > 0) return fromRequest.trim();
131
+ return undefined;
132
+ }
133
+
134
+ const DEVICE_TOKEN_FILE_MAX_BYTES = 1024;
135
+
136
+ const DEVICE_TOKEN_FILE_TTL_MS = 5 * 60_000;
137
+
138
+ let deviceTokenFileCache: { readonly value: string | undefined; readonly readAt: number } | undefined;
139
+
140
+ export function readBuddyDeviceToken(context?: ProviderDispatchContext): string | undefined {
141
+ const state = context?.credential.auth_state;
142
+ const value = state?.["deviceToken"] ?? state?.["device_token"];
143
+ if (typeof value === "string" && value.trim().length > 0) return value.trim();
144
+ return readDeviceTokenFile();
145
+ }
146
+
147
+ export async function buddyDeviceTokenHeaders(args: {
148
+ realm: "intl" | "cn";
149
+ context: ProviderDispatchContext | undefined;
150
+ cliIdentity: boolean | undefined;
151
+ }): Promise<Record<string, string>> {
152
+ const { context, cliIdentity } = args;
153
+ void args.realm; // the desktop client sends the token on both realms
154
+ if (resolveBuddyIdentity(context, cliIdentity) === "cli") return {};
155
+ const token = readBuddyDeviceToken(context);
156
+ if (token !== undefined) return { "x-device-token": token };
157
+ return { "x-device-token-error": "sdk_not_initialized" };
158
+ }
159
+
160
+ function readDeviceTokenFile(): string | undefined {
161
+ const now = Date.now();
162
+ if (deviceTokenFileCache !== undefined && now - deviceTokenFileCache.readAt < DEVICE_TOKEN_FILE_TTL_MS) {
163
+ return deviceTokenFileCache.value;
164
+ }
165
+ const path = process.env["CARTETHYIA_DEVICE_TOKEN_FILE"] ?? "/app/data/device_token";
166
+ let value: string | undefined;
167
+ try {
168
+ const raw = readFileSync(path);
169
+ if (raw.byteLength > 0 && raw.byteLength <= DEVICE_TOKEN_FILE_MAX_BYTES) {
170
+ const text = raw.toString("utf8").trim();
171
+ if (text.length > 0) value = text;
172
+ }
173
+ } catch {
174
+ value = undefined;
175
+ }
176
+ deviceTokenFileCache = { value, readAt: now };
177
+ return value;
178
+ }
179
+
180
+ // ── buddy-cli-headers ──────────────────────────────────────────
181
+
182
+ export function buddyCliTraceHeaders(
183
+ conversation: Readonly<Record<string, string>>,
184
+ ): Record<string, string> {
185
+ const traceId = conversation["x-trace-id"] ?? conversation["x-b3-traceid"];
186
+ const spanId = conversation["x-b3-spanid"];
187
+ if (traceId === undefined) return {};
188
+ const b3 = `${traceId}-${spanId ?? traceId.slice(0, 16)}-1-${traceId.slice(0, 16)}`;
189
+ return { ...(spanId === undefined ? {} : { "X-B3-ParentSpanId": spanId }), b3 };
190
+ }
191
+
192
+ export function buddyCliHeaders(args: {
193
+ readonly userId: string;
194
+ readonly enterpriseId?: string | undefined;
195
+ readonly privateData?: boolean;
196
+ }): Record<string, string> {
197
+ const enterpriseId =
198
+ args.enterpriseId !== undefined && args.enterpriseId.trim().length > 0
199
+ ? args.enterpriseId.trim()
200
+ : undefined;
201
+ return {
202
+ "X-Agent-Intent": "craft",
203
+ "X-Agent-Type": "main",
204
+ // The reference enum is local-agent / cloud-agent / agent-agent, and its
205
+ // parser returns "other" for anything unknown or absent. A gateway is none
206
+ // of the three, so "other" is the honest value rather than a guess.
207
+ "X-Agent-Runtime": "other",
208
+ "X-User-Id": args.userId,
209
+ ...(args.privateData === true ? { "X-Private-Data": "true" } : {}),
210
+ ...(enterpriseId === undefined
211
+ ? {}
212
+ : {
213
+ "X-Enterprise-Id": enterpriseId,
214
+ // The tenant falls back to the enterprise: the two are the same value
215
+ // for an account that belongs to one enterprise, and the upstream
216
+ // reads both.
217
+ "X-Tenant-Id": enterpriseId,
218
+ }),
219
+ };
220
+ }
221
+
222
+ // ── buddy-conversation-headers ──────────────────────────────────────────
223
+
224
+ function newMessageId(): string {
225
+ return crypto.randomUUID().replaceAll("-", "");
226
+ }
227
+
228
+ function toUuidHex(hex: string): string {
229
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
230
+ }
231
+
232
+ function isValidTraceId(value: string): boolean {
233
+ if (value.length !== 16 && value.length !== 32) return false;
234
+ return /^[0-9a-fA-F]+$/.test(value);
235
+ }
236
+
237
+ export interface BuddyConversationIds {
238
+ /**
239
+ * Conversation-scoped id, stable across turns. `undefined` means the client
240
+ * sent none, and the header is then omitted rather than fabricated.
241
+ */
242
+ readonly conversationId?: string | undefined;
243
+ /**
244
+ * Turn-scoped aggregation key. When absent a fresh one is generated, because
245
+ * the header is mandatory and a call without it is what drew `11128`.
246
+ */
247
+ readonly conversationRequestId?: string | undefined;
248
+ /** Inbound trace id to pass through; falls back to the conversation request id. */
249
+ readonly traceId?: string | undefined;
250
+ /**
251
+ * Wire format for the two message-scoped ids (`x-conversation-message-id`,
252
+ * `x-request-id`).
253
+ *
254
+ * The reference emits these as dashed UUIDs on the desktop product
255
+ * and as 32-hex on CLI traffic (`build_attempt_headers` converts only the
256
+ * message/request ids, and only for the desktop profiles). The turn
257
+ * aggregation key (`x-conversation-request-id` / `x-root-request-id`) and the
258
+ * B3/W3C trace ids stay hex in both styles, because B3's spec accepts only
259
+ * hex. Default `"hex"`.
260
+ */
261
+ readonly messageIdStyle?: "hex" | "uuid" | undefined;
262
+ }
263
+
264
+ export function buddyConversationHeaders(
265
+ ids: BuddyConversationIds = {},
266
+ ): Record<string, string> {
267
+ const conversationRequestId =
268
+ ids.conversationRequestId && ids.conversationRequestId.trim().length > 0
269
+ ? ids.conversationRequestId.trim()
270
+ : newMessageId();
271
+ const messageId = newMessageId();
272
+ // The message-scoped ids may render as a dashed UUID (WorkBuddy desktop) or
273
+ // as 32-hex (CLI); the raw hex is still what the B3/W3C trace ids derive from.
274
+ const messageHeaderValue = ids.messageIdStyle === "uuid" ? toUuidHex(messageId) : messageId;
275
+ const traceId =
276
+ ids.traceId && ids.traceId.trim().length > 0 ? ids.traceId.trim() : conversationRequestId;
277
+ const b3TraceId = isValidTraceId(conversationRequestId) ? conversationRequestId : messageId;
278
+
279
+ const headers: Record<string, string> = {
280
+ "x-conversation-request-id": conversationRequestId,
281
+ "x-conversation-message-id": messageHeaderValue,
282
+ "x-request-id": messageHeaderValue,
283
+ "x-root-request-id": conversationRequestId,
284
+ "x-trace-id": traceId,
285
+ // W3C trace context alongside the B3 set below: the upstream reads both,
286
+ // and every reference request carries `traceparent`, so sending only the
287
+ // B3 half leaves a header the channel check expects absent.
288
+ traceparent: `00-${b3TraceId}-${messageId.slice(0, 16)}-01`,
289
+ "x-b3-traceid": b3TraceId,
290
+ "x-b3-spanid": messageId.slice(0, 16),
291
+ "x-b3-sampled": "1",
292
+ };
293
+ // Always sent, invented when the caller gave none.
294
+ //
295
+ // `x-conversation-id` is mandatory, and a chat request without it is refused
296
+ // with `400 · 11-128 "request illegal"` — an error that names neither the
297
+ // header nor the channel, and which reads like a block rather than a missing
298
+ // field. Isolated against the live API by removing one header at a time:
299
+ // only `x-conversation-id`, `x-conversation-request-id` and
300
+ // `x-conversation-message-id` are required; the B3/W3C trace set, the
301
+ // stainless set, the agent block and the IDE block are all optional.
302
+ //
303
+ // Inventing one when the caller has none is still the right default: the id
304
+ // is per-dispatch here, so a caller with no session is not claiming a session
305
+ // it never had — and withholding the header only guarantees a refusal.
306
+ headers["x-conversation-id"] =
307
+ ids.conversationId !== undefined && ids.conversationId.trim().length > 0
308
+ ? ids.conversationId.trim()
309
+ : newMessageId();
310
+ return headers;
311
+ }