cartethyia 2.7.8 → 2.8.0

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 (519) hide show
  1. package/.env.example +3 -3
  2. package/README.md +29 -59
  3. package/dist/dashboard/assets/BannedPage-Cc0vKpMG.js +1 -0
  4. package/dist/dashboard/assets/BannedPage-Cc0vKpMG.js.gz +0 -0
  5. package/dist/dashboard/assets/{CliToolDetailPage-BRWKPQPf.js → CliToolDetailPage-Bqj2aa1T.js} +1 -1
  6. package/dist/dashboard/assets/CliToolDetailPage-Bqj2aa1T.js.gz +0 -0
  7. package/dist/dashboard/assets/{CliToolsPage-BP-Y5iHN.js → CliToolsPage-CXsaOAO2.js} +1 -1
  8. package/dist/dashboard/assets/CliToolsPage-CXsaOAO2.js.gz +0 -0
  9. package/dist/dashboard/assets/CombosPage-BeLpJ4ma.js +1 -0
  10. package/dist/dashboard/assets/CombosPage-BeLpJ4ma.js.gz +0 -0
  11. package/dist/dashboard/assets/ConsoleLogPage-DnV0uZ3m.js +1 -0
  12. package/dist/dashboard/assets/ConsoleLogPage-DnV0uZ3m.js.gz +0 -0
  13. package/dist/dashboard/assets/CustomizationPage-CBxAjo63.js +1 -0
  14. package/dist/dashboard/assets/CustomizationPage-CBxAjo63.js.gz +0 -0
  15. package/dist/dashboard/assets/HealthEventsModal-B5yEE0k6.js +1 -0
  16. package/dist/dashboard/assets/HealthEventsModal-B5yEE0k6.js.gz +0 -0
  17. package/dist/dashboard/assets/{InjectSystemPromptPanel-Cv-2Ol_K.js → InjectSystemPromptPanel-BWFLECmz.js} +2 -2
  18. package/dist/dashboard/assets/InjectSystemPromptPanel-BWFLECmz.js.gz +0 -0
  19. package/dist/dashboard/assets/{LoginPage-bKfz_b1K.js → LoginPage-CgoPc5B9.js} +1 -1
  20. package/dist/dashboard/assets/LoginPage-CgoPc5B9.js.gz +0 -0
  21. package/dist/dashboard/assets/{ModelPicker-B-WQxx_H.js → ModelPicker-D9bFQern.js} +1 -1
  22. package/dist/dashboard/assets/ModelPicker-D9bFQern.js.gz +0 -0
  23. package/dist/dashboard/assets/OverviewPage-B_p4tL8k.js +1 -0
  24. package/dist/dashboard/assets/OverviewPage-B_p4tL8k.js.gz +0 -0
  25. package/dist/dashboard/assets/ProviderDetailPage-rs5E2rxv.js +8 -0
  26. package/dist/dashboard/assets/ProviderDetailPage-rs5E2rxv.js.gz +0 -0
  27. package/dist/dashboard/assets/ProviderIcon-D_0PiZtg.js +1 -0
  28. package/dist/dashboard/assets/ProviderIcon-D_0PiZtg.js.gz +0 -0
  29. package/dist/dashboard/assets/ProvidersPage-CLI2bJTK.js +1 -0
  30. package/dist/dashboard/assets/ProvidersPage-CLI2bJTK.js.gz +0 -0
  31. package/dist/dashboard/assets/{ProxyPage-emlTuxz6.js → ProxyPage-BwNC_-Cx.js} +1 -1
  32. package/dist/dashboard/assets/ProxyPage-BwNC_-Cx.js.gz +0 -0
  33. package/dist/dashboard/assets/QuotaPage-DA-a1iSN.js +1 -0
  34. package/dist/dashboard/assets/QuotaPage-DA-a1iSN.js.gz +0 -0
  35. package/dist/dashboard/assets/SettingsPage-CctTga2C.js +1 -0
  36. package/dist/dashboard/assets/SettingsPage-CctTga2C.js.gz +0 -0
  37. package/dist/dashboard/assets/{SetupPage-CUZrKs9r.js → SetupPage-febLU4r1.js} +1 -1
  38. package/dist/dashboard/assets/SetupPage-febLU4r1.js.gz +0 -0
  39. package/dist/dashboard/assets/{StudioPage-jWmUdDpy.js → StudioPage-M6fvR5Wz.js} +1 -1
  40. package/dist/dashboard/assets/StudioPage-M6fvR5Wz.js.gz +0 -0
  41. package/dist/dashboard/assets/UsagePage-nyeuRqz9.js +56 -0
  42. package/dist/dashboard/assets/UsagePage-nyeuRqz9.js.gz +0 -0
  43. package/dist/dashboard/assets/api-keys-BFOlvrJC.js +1 -0
  44. package/dist/dashboard/assets/api-keys-BFOlvrJC.js.gz +0 -0
  45. package/dist/dashboard/assets/app-Dx2QgsNj.css +2 -0
  46. package/dist/dashboard/assets/app-Dx2QgsNj.css.gz +0 -0
  47. package/dist/dashboard/assets/{assert-CidQVoNo.js → assert-CqTLw5e7.js} +1 -1
  48. package/dist/dashboard/assets/assert-CqTLw5e7.js.gz +0 -0
  49. package/dist/dashboard/assets/{badge-DyI_mJfi.js → badge-C83EqLB0.js} +1 -1
  50. package/dist/dashboard/assets/badge-C83EqLB0.js.gz +0 -0
  51. package/dist/dashboard/assets/ban-durations-B58FyRxC.js +1 -0
  52. package/dist/dashboard/assets/ban-durations-B58FyRxC.js.gz +0 -0
  53. package/dist/dashboard/assets/{banner-B-yXtCqb.js → banner-D1xn7pIe.js} +1 -1
  54. package/dist/dashboard/assets/banner-D1xn7pIe.js.gz +0 -0
  55. package/dist/dashboard/assets/brand/bansos_page.webp +0 -0
  56. package/dist/dashboard/assets/brand/denia_bansos.webp +0 -0
  57. package/dist/dashboard/assets/brand/favicon.webp +0 -0
  58. package/dist/dashboard/assets/brand/login_register.webp +0 -0
  59. package/dist/dashboard/assets/brand/login_register_initial.webp +0 -0
  60. package/dist/dashboard/assets/brand/og_bansos.webp +0 -0
  61. package/dist/dashboard/assets/brand/og_image.webp +0 -0
  62. package/dist/dashboard/assets/brand/sidebar_ico.webp +0 -0
  63. package/dist/dashboard/assets/{card-_HwAEqFF.js → card-DU3Lq5Lm.js} +1 -1
  64. package/dist/dashboard/assets/card-DU3Lq5Lm.js.gz +0 -0
  65. package/dist/dashboard/assets/cli-tools-Dw1U_drK.js +1 -0
  66. package/dist/dashboard/assets/cli-tools-Dw1U_drK.js.gz +0 -0
  67. package/dist/dashboard/assets/{clipboard-button-ByjCk0o9.js → clipboard-button-CEL5Sg_J.js} +1 -1
  68. package/dist/dashboard/assets/clipboard-button-CEL5Sg_J.js.gz +0 -0
  69. package/dist/dashboard/assets/{dialog-BZhOlfz-.js → dialog-BjFR5UHy.js} +1 -1
  70. package/dist/dashboard/assets/dialog-BjFR5UHy.js.gz +0 -0
  71. package/dist/dashboard/assets/{drawer-BADqIRYP.js → drawer-HvBW0AjI.js} +1 -1
  72. package/dist/dashboard/assets/drawer-HvBW0AjI.js.gz +0 -0
  73. package/dist/dashboard/assets/entry-BSu3gFlt.js +4 -0
  74. package/dist/dashboard/assets/entry-BSu3gFlt.js.gz +0 -0
  75. package/dist/dashboard/assets/{entry-COaC-irN.js → entry-CECVELyy.js} +1 -1
  76. package/dist/dashboard/assets/entry-CECVELyy.js.gz +0 -0
  77. package/dist/dashboard/assets/entry-DYYofQ7W.js +1 -0
  78. package/dist/dashboard/assets/entry-DYYofQ7W.js.gz +0 -0
  79. package/dist/dashboard/assets/{formatters-DcHJyRiu.js → formatters-DLW4i8zK.js} +1 -1
  80. package/dist/dashboard/assets/formatters-DLW4i8zK.js.gz +0 -0
  81. package/dist/dashboard/assets/{index-CgKmFngv.js → index-1-fCqe64.js} +2 -2
  82. package/dist/dashboard/assets/index-1-fCqe64.js.gz +0 -0
  83. package/dist/dashboard/assets/{inline-B2BirKry.js → inline-DGvU2oLK.js} +1 -1
  84. package/dist/dashboard/assets/inline-DGvU2oLK.js.gz +0 -0
  85. package/dist/dashboard/assets/{input-D-qV9dKY.js → input-iNnO1X2K.js} +1 -1
  86. package/dist/dashboard/assets/input-iNnO1X2K.js.gz +0 -0
  87. package/dist/dashboard/assets/{layout-B7tVWhtV.js → layout-0q8UGypb.js} +1 -1
  88. package/dist/dashboard/assets/layout-0q8UGypb.js.gz +0 -0
  89. package/dist/dashboard/assets/{list-row-CtkGE3bF.js → list-row-C0w2bQjJ.js} +1 -1
  90. package/dist/dashboard/assets/list-row-C0w2bQjJ.js.gz +0 -0
  91. package/dist/dashboard/assets/{live-DGizF06-.js → live-M5aJ_xCa.js} +1 -1
  92. package/dist/dashboard/assets/live-M5aJ_xCa.js.gz +0 -0
  93. package/dist/dashboard/assets/{mutation-GU7XdEjz.js → mutation-BQ0ccH_A.js} +1 -1
  94. package/dist/dashboard/assets/mutation-BQ0ccH_A.js.gz +0 -0
  95. package/dist/dashboard/assets/{network-C9UE-bOr.js → network-BKeQGT3K.js} +1 -1
  96. package/dist/dashboard/assets/network-BKeQGT3K.js.gz +0 -0
  97. package/dist/dashboard/assets/{numbers-BnAd22a_.js → numbers-CivAk3da.js} +1 -1
  98. package/dist/dashboard/assets/numbers-CivAk3da.js.gz +0 -0
  99. package/dist/dashboard/assets/{pagination-BAywyMzS.js → pagination-CpzvI5ep.js} +1 -1
  100. package/dist/dashboard/assets/pagination-CpzvI5ep.js.gz +0 -0
  101. package/dist/dashboard/assets/providers/devin.svg +9 -0
  102. package/dist/dashboard/assets/providers/devin.svg.gz +0 -0
  103. package/dist/dashboard/assets/providers/windsurf.svg +10 -0
  104. package/dist/dashboard/assets/providers/windsurf.svg.gz +0 -0
  105. package/dist/dashboard/assets/providers/zro.svg +3 -0
  106. package/dist/dashboard/assets/providers/zro.svg.gz +0 -0
  107. package/dist/dashboard/assets/{query-keys-YSoQOUh6.js → query-keys-IKLh0YFd.js} +1 -1
  108. package/dist/dashboard/assets/query-keys-IKLh0YFd.js.gz +0 -0
  109. package/dist/dashboard/assets/quota-DiPD6UKH.js +1 -0
  110. package/dist/dashboard/assets/quota-DiPD6UKH.js.gz +0 -0
  111. package/dist/dashboard/assets/routing-BFQX2VBc.js +1 -0
  112. package/dist/dashboard/assets/routing-BFQX2VBc.js.gz +0 -0
  113. package/dist/dashboard/assets/{select-BCOcv8WK.js → select-CXbck0jG.js} +2 -2
  114. package/dist/dashboard/assets/select-CXbck0jG.js.gz +0 -0
  115. package/dist/dashboard/assets/settings-C_XvMHh9.js +1 -0
  116. package/dist/dashboard/assets/settings-C_XvMHh9.js.gz +0 -0
  117. package/dist/dashboard/assets/{stack-ByKt3Q1g.js → stack-DggRiCxE.js} +1 -1
  118. package/dist/dashboard/assets/stack-DggRiCxE.js.gz +0 -0
  119. package/dist/dashboard/assets/{state-DXbGTx7N.js → state-ClFzfbYp.js} +1 -1
  120. package/dist/dashboard/assets/state-ClFzfbYp.js.gz +0 -0
  121. package/dist/dashboard/assets/story/25817331.webp +0 -0
  122. package/dist/dashboard/assets/story/Shorekeeper.webp +0 -0
  123. package/dist/dashboard/assets/story/cartethyia-god.webp +0 -0
  124. package/dist/dashboard/assets/story/fleurdelys_plus.webp +0 -0
  125. package/dist/dashboard/assets/story/jinhsi-blossom.webp +0 -0
  126. package/dist/dashboard/assets/story/phrolova-a.webp +0 -0
  127. package/dist/dashboard/assets/story/requestdeniawokkjpg.webp +0 -0
  128. package/dist/dashboard/assets/story/wuthering-waves-hiyuki-aemeath-hiyuki-rover.webp +0 -0
  129. package/dist/dashboard/assets/{switch-ZeTsW623.js → switch-CTIFxKqS.js} +1 -1
  130. package/dist/dashboard/assets/switch-CTIFxKqS.js.gz +0 -0
  131. package/dist/dashboard/assets/{useMutation-CbCP4w1o.js → useMutation-BClG3yWj.js} +1 -1
  132. package/dist/dashboard/assets/useMutation-BClG3yWj.js.gz +0 -0
  133. package/dist/dashboard/console-backgrounds.webp +0 -0
  134. package/dist/dashboard/console-backgrounds_initial.webp +0 -0
  135. package/dist/dashboard/donation/qris-risun-marke.webp +0 -0
  136. package/dist/dashboard/index.html +3 -3
  137. package/dist/dashboard/index.html.gz +0 -0
  138. package/dist/dashboard/precompressed-assets.json +59 -56
  139. package/migrations/full-postgres/0044_add_models_warning_metadata.sql +10 -0
  140. package/migrations/full-postgres/0045_migrate_workbuddy_to_codebuddy.sql +156 -0
  141. package/migrations/full-postgres/0046_add_accounts_quota_plan_fetched_at.sql +7 -0
  142. package/migrations/lite-sqlite3/0044_add_models_warning_metadata.sql +11 -0
  143. package/migrations/lite-sqlite3/0045_migrate_workbuddy_to_codebuddy.sql +156 -0
  144. package/migrations/lite-sqlite3/0046_add_accounts_quota_plan_fetched_at.sql +4 -0
  145. package/package.json +1 -1
  146. package/src/boot/app.ts +10 -13
  147. package/src/boot/dependencies.ts +12 -4
  148. package/src/boot/flush.ts +20 -0
  149. package/src/boot/lifecycle.ts +7 -5
  150. package/src/config/cooldowns.ts +1 -1
  151. package/src/config/routing.ts +26 -0
  152. package/src/config/spec-metadata.ts +72 -0
  153. package/src/config/spec.ts +20 -6
  154. package/src/console/api-keys/contracts.ts +12 -1
  155. package/src/console/api-keys/routes.ts +50 -31
  156. package/src/console/auth/account-reset.ts +7 -9
  157. package/src/console/backup/contracts.ts +4 -0
  158. package/src/console/backup/export-endpoint.ts +7 -5
  159. package/src/console/cli-tools/service.ts +6 -12
  160. package/src/console/console-router-guards.ts +23 -16
  161. package/src/console/console-router.ts +7 -0
  162. package/src/console/dashboard-assets.ts +5 -5
  163. package/src/console/domain-registration.ts +27 -2
  164. package/src/console/inferhub/normalize.ts +24 -28
  165. package/src/console/inferhub/types.ts +3 -0
  166. package/src/console/ip-bans/routes.ts +93 -0
  167. package/src/console/observability/store.ts +0 -53
  168. package/src/console/providers/catalog/catalog-projections.ts +5 -2
  169. package/src/console/providers/catalog/contracts.ts +123 -0
  170. package/src/console/providers/catalog/discover-constants.ts +6 -0
  171. package/src/console/providers/catalog/model-operations.ts +122 -10
  172. package/src/console/providers/catalog/provider-operations.ts +116 -8
  173. package/src/console/providers/catalog/public-model-store.ts +0 -0
  174. package/src/console/providers/catalog/routes.ts +65 -4
  175. package/src/console/providers/catalog/store-helpers.ts +30 -1
  176. package/src/console/providers/catalog/store.ts +249 -4
  177. package/src/console/quota/account-quota-global.ts +97 -19
  178. package/src/console/quota/account-quota-shared.ts +49 -0
  179. package/src/console/quota/account-quota-tenant.ts +6 -18
  180. package/src/console/quota/native-identity.ts +2 -0
  181. package/src/console/quota/quota-view.ts +19 -3
  182. package/src/console/routing/pools/operations.ts +31 -46
  183. package/src/console/settings/contracts.ts +18 -0
  184. package/src/console/settings/store.ts +32 -0
  185. package/src/console/shared/errors.ts +33 -6
  186. package/src/console/system-prompt/contracts.ts +0 -1
  187. package/src/main.ts +3 -3
  188. package/src/network/pool/byte-accounting.ts +3 -2
  189. package/src/network/pool/selector.ts +182 -39
  190. package/src/network/pool-cooldown-store.ts +89 -14
  191. package/src/network/pool-health.ts +49 -61
  192. package/src/network/rate-limit-verdict.ts +1 -1
  193. package/src/network/ssrf.ts +6 -6
  194. package/src/observability/event-log.ts +7 -361
  195. package/src/observability/event-query.ts +18 -4
  196. package/src/observability/performance-metrics.ts +9 -0
  197. package/src/observability/runtime-metrics.ts +14 -9
  198. package/src/observability/telemetry-buffer.ts +15 -25
  199. package/src/observability/telemetry-reconciliation.ts +9 -37
  200. package/src/observability/telemetry-row.ts +125 -0
  201. package/src/persistence/db-mode.ts +4 -1
  202. package/src/persistence/dialect.ts +30 -0
  203. package/src/persistence/migrate.ts +6 -3
  204. package/src/persistence/schema.ts +28 -2
  205. package/src/persistence/ttl-cache.ts +20 -2
  206. package/src/protocol/codecs.ts +1 -1
  207. package/src/protocol/dsml.ts +35 -87
  208. package/src/protocol/gateway-error.ts +1 -0
  209. package/src/protocol/markup-stream-parser.ts +195 -0
  210. package/src/protocol/reasoning-text.ts +19 -0
  211. package/src/protocol/reasoning.ts +1 -1
  212. package/src/protocol/request/chat.ts +14 -14
  213. package/src/protocol/request/gemini.ts +8 -2
  214. package/src/protocol/request/messages.ts +4 -5
  215. package/src/protocol/response/chat.ts +12 -20
  216. package/src/protocol/response/gemini.ts +6 -1
  217. package/src/protocol/response/omp-tool-call-markup.ts +43 -86
  218. package/src/protocol/response/responses.ts +18 -2
  219. package/src/protocol/sse-decoder.ts +25 -8
  220. package/src/protocol/stop-reason.ts +23 -1
  221. package/src/protocol/stream-error-frames.ts +13 -3
  222. package/src/protocol/think-split.ts +112 -0
  223. package/src/protocol/translation/dialect/qwen.ts +26 -58
  224. package/src/protocol/translation/quirks.ts +1 -1
  225. package/src/protocol/translation/tool-repair.ts +2 -2
  226. package/src/protocol/upstream/send-claude-messages.ts +15 -4
  227. package/src/protocol/upstream-parsing.ts +1 -1
  228. package/src/providers/adapters/compatible-adapter.ts +78 -17
  229. package/src/providers/authentication/oauth-client.ts +1 -1
  230. package/src/providers/authentication/oauth-device-flow.ts +98 -8
  231. package/src/providers/authentication/oauth-refresh-service.ts +153 -59
  232. package/src/providers/configured-provider.ts +148 -0
  233. package/src/providers/default-registry.ts +117 -27
  234. package/src/providers/discovery/discovery-types.ts +12 -0
  235. package/src/providers/discovery/probe-phases.ts +74 -33
  236. package/src/providers/discovery/probing-service.ts +293 -165
  237. package/src/providers/integrations/alibabacp/alibabacp-models.ts +1 -2
  238. package/src/providers/integrations/{dashscope-models.ts → alibabacp/dashscope-models.ts} +2 -2
  239. package/src/providers/integrations/antigravity/antigravity-protocol.ts +121 -2
  240. package/src/providers/integrations/antigravity/antigravity.ts +13 -2
  241. package/src/providers/integrations/buddy/buddy-catalog-shared.ts +72 -34
  242. package/src/providers/integrations/buddy/buddy-chat-shared.ts +191 -35
  243. package/src/providers/integrations/buddy/buddy-checkin.ts +17 -39
  244. package/src/providers/integrations/buddy/buddy-conversation-headers.ts +2 -2
  245. package/src/providers/integrations/buddy/buddy-discovery-shared.ts +5 -5
  246. package/src/providers/integrations/buddy/buddy-identity.ts +89 -6
  247. package/src/providers/integrations/buddy/buddy-media.ts +4 -5
  248. package/src/providers/integrations/buddy/buddy-oauth-shared.ts +2 -2
  249. package/src/providers/integrations/buddy/buddy-quota-shared.ts +1 -1
  250. package/src/providers/integrations/buddy/buddy-usage-shared.ts +1 -3
  251. package/src/providers/integrations/buddy/capabilities.ts +6 -43
  252. package/src/providers/integrations/buddy/codebuddy/cn-catalog.ts +2 -1
  253. package/src/providers/integrations/buddy/codebuddy/cn-provider.ts +23 -1
  254. package/src/providers/integrations/buddy/codebuddy/provider.ts +6 -2
  255. package/src/providers/integrations/buddy/codebuddy/shared.ts +38 -27
  256. package/src/providers/integrations/claude/claude-quota.ts +14 -17
  257. package/src/providers/integrations/cline/cline.ts +2 -2
  258. package/src/providers/integrations/commandcode/quota.ts +22 -28
  259. package/src/providers/integrations/cursor/cursor-auth.ts +0 -8
  260. package/src/providers/integrations/cursor/cursor-catalog.ts +2 -0
  261. package/src/providers/integrations/cursor/cursor-exec.ts +745 -0
  262. package/src/providers/integrations/cursor/cursor-http2.ts +2 -0
  263. package/src/providers/integrations/cursor/cursor-request.ts +35 -20
  264. package/src/providers/integrations/cursor/cursor-shared.ts +26 -5
  265. package/src/providers/integrations/cursor/cursor.ts +139 -70
  266. package/src/providers/integrations/cursor/protocol/messages.ts +22 -29
  267. package/src/providers/integrations/cursor/protocol/protobuf-schema.ts +37 -0
  268. package/src/providers/integrations/cursor/protocol/tool-canonical.ts +26 -100
  269. package/src/providers/integrations/devin/devin-auth.ts +353 -0
  270. package/src/providers/integrations/devin/devin-catalog.ts +207 -0
  271. package/src/providers/integrations/devin/devin-http.ts +91 -0
  272. package/src/providers/integrations/devin/devin-quota.ts +214 -0
  273. package/src/providers/integrations/devin/devin-shared.ts +111 -0
  274. package/src/providers/integrations/devin/devin.ts +539 -0
  275. package/src/providers/integrations/devin/protocol/devin-proto.ts +558 -0
  276. package/src/providers/integrations/devin/protocol/framing.ts +124 -0
  277. package/src/providers/integrations/factory/factory-oauth.ts +17 -44
  278. package/src/providers/integrations/factory/factory.ts +2 -2
  279. package/src/providers/integrations/freebuff/assets/official-system-manager.txt +88 -0
  280. package/src/providers/integrations/freebuff/assets/official-system-worker.txt +57 -0
  281. package/src/providers/integrations/freebuff/assets/official-tools.json +1070 -0
  282. package/src/providers/integrations/freebuff/freebuff-catalog.ts +120 -0
  283. package/src/providers/integrations/freebuff/freebuff-constants.ts +1233 -0
  284. package/src/providers/integrations/freebuff/freebuff-discovery.ts +108 -0
  285. package/src/providers/integrations/freebuff/freebuff-oauth.ts +240 -0
  286. package/src/providers/integrations/freebuff/freebuff-quota.ts +260 -0
  287. package/src/providers/integrations/freebuff/freebuff-virtual.ts +89 -0
  288. package/src/providers/integrations/freebuff/freebuff.ts +682 -0
  289. package/src/providers/integrations/freepi/freepi-catalog.ts +60 -0
  290. package/src/providers/integrations/freepi/freepi-discovery.ts +82 -0
  291. package/src/providers/integrations/freepi/freepi-headers.ts +21 -0
  292. package/src/providers/integrations/freepi/freepi-oauth.ts +142 -0
  293. package/src/providers/integrations/freepi/freepi-protocol.ts +242 -0
  294. package/src/providers/integrations/freepi/freepi-quota.ts +126 -0
  295. package/src/providers/integrations/freepi/freepi-realm.ts +35 -0
  296. package/src/providers/integrations/freepi/freepi.ts +292 -0
  297. package/src/providers/integrations/gemini.ts +2 -2
  298. package/src/providers/integrations/grok/grok-oauth.ts +48 -45
  299. package/src/providers/integrations/grok/grok-ratelimit-store.ts +10 -17
  300. package/src/providers/integrations/grok/grok-turn-index.ts +11 -16
  301. package/src/providers/integrations/hermes/hermes-oauth.ts +94 -0
  302. package/src/providers/integrations/{hermes.ts → hermes/hermes.ts} +13 -21
  303. package/src/providers/integrations/hermes/index.ts +2 -0
  304. package/src/providers/integrations/kiro/kiro-catalog.ts +17 -1
  305. package/src/providers/integrations/kiro/kiro-discovery.ts +10 -12
  306. package/src/providers/integrations/kiro/kiro-quota.ts +8 -4
  307. package/src/providers/integrations/kiro/kiro.ts +15 -19
  308. package/src/providers/integrations/ollamacloud/index.ts +2 -0
  309. package/src/providers/integrations/{ollamacloud-models.ts → ollamacloud/models.ts} +2 -2
  310. package/src/providers/integrations/{ollamacloud-quota.ts → ollamacloud/quota.ts} +3 -3
  311. package/src/providers/integrations/opencode/fingerprint.ts +1 -1
  312. package/src/providers/integrations/opencode/opencode-go-quota.ts +2 -8
  313. package/src/providers/integrations/perch/perch-catalog.ts +105 -0
  314. package/src/providers/integrations/perch/perch-credential.ts +25 -0
  315. package/src/providers/integrations/perch/perch-oauth.ts +170 -0
  316. package/src/providers/integrations/perch/perch-protocol.ts +208 -0
  317. package/src/providers/integrations/perch/perch-quota.ts +123 -0
  318. package/src/providers/integrations/perch/perch-realm.ts +82 -0
  319. package/src/providers/integrations/perch/perch-system-prompt.ts +13 -0
  320. package/src/providers/integrations/perch/perch.ts +305 -0
  321. package/src/providers/integrations/perch/saffron-system-prompt.txt +8 -0
  322. package/src/providers/integrations/qoder/chat-proto.ts +328 -328
  323. package/src/providers/integrations/qoder/claim-qoder-credits.ts +19 -60
  324. package/src/providers/integrations/qoder/qoder-adapter.ts +90 -40
  325. package/src/providers/integrations/qoder/qoder-auth.ts +101 -29
  326. package/src/providers/integrations/qoder/qoder-cache.ts +32 -0
  327. package/src/providers/integrations/qoder/qoder-catalog.ts +26 -6
  328. package/src/providers/integrations/qoder/qoder-chat.ts +7 -21
  329. package/src/providers/integrations/qoder/qoder-checkin.ts +6 -6
  330. package/src/providers/integrations/qoder/qoder-grpc.ts +210 -7
  331. package/src/providers/integrations/qoder/qoder-ide.ts +5 -108
  332. package/src/providers/integrations/qoder/qoder-identity-cache.ts +184 -0
  333. package/src/providers/integrations/qoder/qoder-native-identity-store.ts +83 -18
  334. package/src/providers/integrations/qoder/qoder-realm.ts +9 -6
  335. package/src/providers/integrations/qoder/qoder-umid.ts +109 -342
  336. package/src/providers/integrations/qoder/qoder-virtual.ts +33 -1
  337. package/src/providers/integrations/qoder/qoder.ts +3 -7
  338. package/src/providers/integrations/trae/trae-catalog.ts +9 -14
  339. package/src/providers/integrations/trae/trae-headers.ts +36 -11
  340. package/src/providers/integrations/trae/trae-oauth.ts +14 -17
  341. package/src/providers/integrations/trae/trae-protocol.ts +12 -11
  342. package/src/providers/integrations/trae/trae-quota.ts +96 -36
  343. package/src/providers/integrations/trae/trae-realm.ts +74 -15
  344. package/src/providers/integrations/trae/trae.ts +99 -26
  345. package/src/providers/integrations/zcode-start-plan/provider.ts +226 -23
  346. package/src/providers/integrations/zcode-start-plan/quota.ts +19 -2
  347. package/src/providers/integrations/zro/zro.ts +204 -0
  348. package/src/providers/operations/account-error-classification.ts +235 -278
  349. package/src/providers/operations/account-health-service.ts +273 -26
  350. package/src/providers/operations/account-refresh-status.ts +269 -0
  351. package/src/providers/operations/account-tier.ts +66 -0
  352. package/src/providers/operations/client-version-resolver.ts +1 -1
  353. package/src/providers/operations/client-versions.ts +42 -150
  354. package/src/providers/operations/cooldown-rules.ts +56 -0
  355. package/src/providers/operations/{model-catalog-cache.ts → provider-caches.ts} +68 -16
  356. package/src/providers/operations/provider-catalog-seeder.ts +4 -0
  357. package/src/providers/operations/provider-catalog-service.ts +1 -1
  358. package/src/providers/operations/provider-credential-service.ts +102 -32
  359. package/src/providers/operations/provider-rules/github.ts +48 -0
  360. package/src/providers/operations/provider-rules/grok.ts +33 -0
  361. package/src/providers/operations/provider-rules/inception.ts +49 -0
  362. package/src/providers/operations/provider-rules/index.ts +32 -0
  363. package/src/providers/operations/provider-rules/perch.ts +37 -0
  364. package/src/providers/operations/provider-rules/qoder.ts +70 -0
  365. package/src/providers/operations/provider-rules/trae.ts +81 -0
  366. package/src/providers/provider-contracts.ts +33 -2
  367. package/src/providers/{provider-credential-resolver.ts → provider-credentials.ts} +40 -3
  368. package/src/providers/provider-metadata.ts +99 -14
  369. package/src/providers/provider-model-definition.ts +36 -0
  370. package/src/providers/provider-probe.ts +15 -1
  371. package/src/providers/provider-registry.ts +42 -33
  372. package/src/providers/quota/quota-limit-windows.ts +16 -5
  373. package/src/providers/quota/quota-support.ts +1 -1
  374. package/src/providers/quota/quota-window-parser.ts +90 -18
  375. package/src/providers/quota/refresh.ts +96 -18
  376. package/src/security/abuse.ts +71 -0
  377. package/src/security/api-key-auth-cache.ts +1 -1
  378. package/src/security/api-key-auth.ts +6 -2
  379. package/src/transport/dispatch/attempt-finalize.ts +14 -13
  380. package/src/transport/dispatch/attempt-loop.ts +71 -17
  381. package/src/transport/dispatch/generation.ts +2 -8
  382. package/src/transport/dispatch/native-shared.ts +25 -0
  383. package/src/transport/dispatch/proxy-request.ts +9 -46
  384. package/src/transport/dispatch/responses-compact.ts +2 -8
  385. package/src/transport/dispatch/streaming/finalize.ts +9 -6
  386. package/src/transport/dispatch/streaming/stream.ts +9 -36
  387. package/src/transport/dispatch/systemone.ts +2 -8
  388. package/src/transport/dispatch/token-count.ts +2 -6
  389. package/src/transport/dispatch/upstream.ts +67 -1
  390. package/src/transport/drain-endpoint.ts +31 -4
  391. package/src/transport/failure-policy.ts +129 -37
  392. package/src/transport/guards/body-policy.ts +7 -1
  393. package/src/transport/guards/error-lifecycle.ts +13 -1
  394. package/src/transport/guards/request-context.ts +22 -14
  395. package/src/transport/request/preparer.ts +81 -71
  396. package/src/transport/routing/bounded-lru.ts +14 -0
  397. package/src/transport/routing/drain-reserve.ts +129 -0
  398. package/src/transport/routing/plan-gate.ts +7 -1
  399. package/src/transport/routing/round-robin.ts +60 -0
  400. package/src/transport/routing/route-catalog.ts +123 -111
  401. package/src/transport/routing/route-model.ts +51 -9
  402. package/src/transport/routing/router.ts +418 -33
  403. package/src/transport/surface/adapters.ts +11 -2
  404. package/src/transport/surface/chat/adapter.ts +2 -2
  405. package/src/transport/surface/chat/encode.ts +47 -4
  406. package/src/transport/surface/chat/parse.ts +2 -12
  407. package/src/transport/surface/completion.ts +44 -33
  408. package/src/transport/surface/content-parts.ts +21 -0
  409. package/src/transport/surface/messages/adapter.ts +9 -2
  410. package/src/transport/surface/messages/encode.ts +11 -3
  411. package/src/transport/surface/messages/parse.ts +2 -1
  412. package/src/transport/surface/messages/stream.ts +33 -17
  413. package/src/transport/surface/responses/encode.ts +34 -6
  414. package/src/transport/surface/responses/parse.ts +3 -11
  415. package/src/workers/daily-checkin.ts +1 -1
  416. package/src/workers/quota-refresh-sweep.ts +1 -1
  417. package/src/workers/scheduler.ts +22 -4
  418. package/dist/dashboard/assets/BannedPage-DeIbIiBU.js +0 -1
  419. package/dist/dashboard/assets/BannedPage-DeIbIiBU.js.gz +0 -0
  420. package/dist/dashboard/assets/CliToolDetailPage-BRWKPQPf.js.gz +0 -0
  421. package/dist/dashboard/assets/CliToolsPage-BP-Y5iHN.js.gz +0 -0
  422. package/dist/dashboard/assets/CombosPage-B7JHozVa.js +0 -1
  423. package/dist/dashboard/assets/CombosPage-B7JHozVa.js.gz +0 -0
  424. package/dist/dashboard/assets/ConsoleLogPage-GX7iGGcu.js +0 -1
  425. package/dist/dashboard/assets/ConsoleLogPage-GX7iGGcu.js.gz +0 -0
  426. package/dist/dashboard/assets/CustomizationPage-HVonbGEa.js +0 -1
  427. package/dist/dashboard/assets/CustomizationPage-HVonbGEa.js.gz +0 -0
  428. package/dist/dashboard/assets/HealthEventsModal-xaut4OJ9.js +0 -1
  429. package/dist/dashboard/assets/HealthEventsModal-xaut4OJ9.js.gz +0 -0
  430. package/dist/dashboard/assets/InjectSystemPromptPanel-Cv-2Ol_K.js.gz +0 -0
  431. package/dist/dashboard/assets/LoginPage-bKfz_b1K.js.gz +0 -0
  432. package/dist/dashboard/assets/ModelPicker-B-WQxx_H.js.gz +0 -0
  433. package/dist/dashboard/assets/OverviewPage-Z9pJZFo8.js +0 -1
  434. package/dist/dashboard/assets/OverviewPage-Z9pJZFo8.js.gz +0 -0
  435. package/dist/dashboard/assets/ProviderDetailPage-CMeDBxR1.js +0 -7
  436. package/dist/dashboard/assets/ProviderDetailPage-CMeDBxR1.js.gz +0 -0
  437. package/dist/dashboard/assets/ProviderIcon-jjqXJDA3.js +0 -1
  438. package/dist/dashboard/assets/ProviderIcon-jjqXJDA3.js.gz +0 -0
  439. package/dist/dashboard/assets/ProvidersPage-BXPdOpG4.js +0 -1
  440. package/dist/dashboard/assets/ProvidersPage-BXPdOpG4.js.gz +0 -0
  441. package/dist/dashboard/assets/ProxyPage-emlTuxz6.js.gz +0 -0
  442. package/dist/dashboard/assets/QuotaPage-CMJKNYWb.js +0 -1
  443. package/dist/dashboard/assets/QuotaPage-CMJKNYWb.js.gz +0 -0
  444. package/dist/dashboard/assets/SettingsPage-BSy7EwGo.js +0 -1
  445. package/dist/dashboard/assets/SettingsPage-BSy7EwGo.js.gz +0 -0
  446. package/dist/dashboard/assets/SetupPage-CUZrKs9r.js.gz +0 -0
  447. package/dist/dashboard/assets/StudioPage-jWmUdDpy.js.gz +0 -0
  448. package/dist/dashboard/assets/UsagePage-5F9tGVGG.js +0 -56
  449. package/dist/dashboard/assets/UsagePage-5F9tGVGG.js.gz +0 -0
  450. package/dist/dashboard/assets/api-keys-KvX83mRe.js +0 -1
  451. package/dist/dashboard/assets/api-keys-KvX83mRe.js.gz +0 -0
  452. package/dist/dashboard/assets/app-BEsU8hKs.css +0 -2
  453. package/dist/dashboard/assets/app-BEsU8hKs.css.gz +0 -0
  454. package/dist/dashboard/assets/assert-CidQVoNo.js.gz +0 -0
  455. package/dist/dashboard/assets/badge-DyI_mJfi.js.gz +0 -0
  456. package/dist/dashboard/assets/banner-B-yXtCqb.js.gz +0 -0
  457. package/dist/dashboard/assets/brand/bansos_page.png +0 -0
  458. package/dist/dashboard/assets/brand/denia_bansos.jpg +0 -0
  459. package/dist/dashboard/assets/brand/favicon.jpg +0 -0
  460. package/dist/dashboard/assets/brand/login_register.jpg +0 -0
  461. package/dist/dashboard/assets/brand/login_register_initial.jpg +0 -0
  462. package/dist/dashboard/assets/brand/og_bansos.jpg +0 -0
  463. package/dist/dashboard/assets/brand/og_image.jpg +0 -0
  464. package/dist/dashboard/assets/brand/sidebar_ico.jpg +0 -0
  465. package/dist/dashboard/assets/card-_HwAEqFF.js.gz +0 -0
  466. package/dist/dashboard/assets/cli-tools-Bwv5-zGZ.js +0 -1
  467. package/dist/dashboard/assets/cli-tools-Bwv5-zGZ.js.gz +0 -0
  468. package/dist/dashboard/assets/clipboard-button-ByjCk0o9.js.gz +0 -0
  469. package/dist/dashboard/assets/dialog-BZhOlfz-.js.gz +0 -0
  470. package/dist/dashboard/assets/drawer-BADqIRYP.js.gz +0 -0
  471. package/dist/dashboard/assets/entry-COaC-irN.js.gz +0 -0
  472. package/dist/dashboard/assets/entry-Ca7LSCet.js +0 -4
  473. package/dist/dashboard/assets/entry-Ca7LSCet.js.gz +0 -0
  474. package/dist/dashboard/assets/entry-PDd27uCd.js +0 -1
  475. package/dist/dashboard/assets/entry-PDd27uCd.js.gz +0 -0
  476. package/dist/dashboard/assets/formatters-DcHJyRiu.js.gz +0 -0
  477. package/dist/dashboard/assets/index-CgKmFngv.js.gz +0 -0
  478. package/dist/dashboard/assets/inline-B2BirKry.js.gz +0 -0
  479. package/dist/dashboard/assets/input-D-qV9dKY.js.gz +0 -0
  480. package/dist/dashboard/assets/layout-B7tVWhtV.js.gz +0 -0
  481. package/dist/dashboard/assets/list-row-CtkGE3bF.js.gz +0 -0
  482. package/dist/dashboard/assets/live-DGizF06-.js.gz +0 -0
  483. package/dist/dashboard/assets/minimize-2-BTmMTK4o.js +0 -1
  484. package/dist/dashboard/assets/minimize-2-BTmMTK4o.js.gz +0 -0
  485. package/dist/dashboard/assets/mutation-GU7XdEjz.js.gz +0 -0
  486. package/dist/dashboard/assets/network-C9UE-bOr.js.gz +0 -0
  487. package/dist/dashboard/assets/numbers-BnAd22a_.js.gz +0 -0
  488. package/dist/dashboard/assets/pagination-BAywyMzS.js.gz +0 -0
  489. package/dist/dashboard/assets/query-keys-YSoQOUh6.js.gz +0 -0
  490. package/dist/dashboard/assets/quota-DJ1QlAOq.js +0 -1
  491. package/dist/dashboard/assets/quota-DJ1QlAOq.js.gz +0 -0
  492. package/dist/dashboard/assets/routing-fnCsxgNO.js +0 -1
  493. package/dist/dashboard/assets/routing-fnCsxgNO.js.gz +0 -0
  494. package/dist/dashboard/assets/select-BCOcv8WK.js.gz +0 -0
  495. package/dist/dashboard/assets/settings-BxslsErn.js +0 -1
  496. package/dist/dashboard/assets/settings-BxslsErn.js.gz +0 -0
  497. package/dist/dashboard/assets/stack-ByKt3Q1g.js.gz +0 -0
  498. package/dist/dashboard/assets/state-DXbGTx7N.js.gz +0 -0
  499. package/dist/dashboard/assets/switch-ZeTsW623.js.gz +0 -0
  500. package/dist/dashboard/assets/useMutation-CbCP4w1o.js.gz +0 -0
  501. package/dist/dashboard/console-backgrounds.jpg +0 -0
  502. package/dist/dashboard/console-backgrounds_initial.jpg +0 -0
  503. package/dist/dashboard/donation/qris-risun-marke.png +0 -0
  504. package/src/providers/bundled-discovery-providers.ts +0 -98
  505. package/src/providers/configured-openai-providers.ts +0 -120
  506. package/src/providers/integrations/buddy/turing-device-token.ts +0 -262
  507. package/src/providers/integrations/buddy/workbuddy/cn-oauth.ts +0 -30
  508. package/src/providers/integrations/buddy/workbuddy/cn-provider.ts +0 -58
  509. package/src/providers/integrations/buddy/workbuddy/oauth.ts +0 -31
  510. package/src/providers/integrations/buddy/workbuddy/provider.ts +0 -209
  511. package/src/providers/integrations/buddy/workbuddy/quota.ts +0 -161
  512. package/src/providers/integrations/buddy/workbuddy/shared.ts +0 -285
  513. package/src/providers/operations/model-discovery-cache.ts +0 -34
  514. package/src/providers/operations/probe-fetch.ts +0 -15
  515. package/src/providers/operations/provider-version-cache.ts +0 -30
  516. package/src/providers/provider-capabilities.ts +0 -33
  517. package/src/providers/provider-credential-envelope.ts +0 -43
  518. /package/dist/dashboard/assets/{app-CTxoqyYB.js → app-DIXLjojg.js} +0 -0
  519. /package/dist/dashboard/assets/{app-CTxoqyYB.js.gz → app-DIXLjojg.js.gz} +0 -0
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Chat-wire payload and message normalization shared by the Tencent "buddy"
3
- * family providers — CodeBuddy International (`cb`), CodeBuddy CN (`cbcn`),
4
- * and WorkBuddy. All three speak the same upstream contract for this part of
3
+ * family providers — CodeBuddy International (`cb`) and CodeBuddy CN (`cbcn`).
4
+ * Both speak the same upstream contract for this part of
5
5
  * the payload, so the rules live here once instead of being copied per
6
6
  * variant:
7
7
  *
@@ -21,6 +21,7 @@ import { createHash } from "node:crypto";
21
21
  import { completeRequiredSchema } from "../../../protocol/codecs";
22
22
  import type { CanonicalRequest } from "../../../protocol/canonical-model";
23
23
  import { isRecord } from "../../../protocol/codecs";
24
+ import { GatewayError } from "../../../protocol/gateway-error";
24
25
  import { applyDeepSeekReasoning, applyOpenAIReasoning, backfillDeepSeekReasoningContent, clampedEffortOutputFloor, ensureChatReasoningReplayPresence } from "../../../protocol/reasoning";
25
26
  import { BUDDY_CN_EFFORTS, BUDDY_INTL_EFFORTS, BUDDY_SHARED_RAW } from "./buddy-catalog-shared";
26
27
  // Imported for the CN roster only: a CN-only id has no row in the shared
@@ -55,6 +56,123 @@ function normalizedToolName(value: string, index: number, used: Set<string>): st
55
56
  return uniqueToolName(base || `tool_${index + 1}`, used);
56
57
  }
57
58
 
59
+ /**
60
+ * Schema keywords whose values are schemas (recursed as schema objects).
61
+ * Mirrors the donor gateway's `upstream_compat._SCHEMA_MAPS/_ARRAYS/CHILDREN`.
62
+ */
63
+ const RE2_SCHEMA_MAPS: ReadonlySet<string> = new Set([
64
+ "properties",
65
+ "patternProperties",
66
+ "$defs",
67
+ "definitions",
68
+ "dependentSchemas",
69
+ "dependencies",
70
+ ]);
71
+ const RE2_SCHEMA_ARRAYS: ReadonlySet<string> = new Set([
72
+ "allOf",
73
+ "anyOf",
74
+ "oneOf",
75
+ "prefixItems",
76
+ "items",
77
+ ]);
78
+ const RE2_SCHEMA_CHILDREN: ReadonlySet<string> = new Set([
79
+ "items",
80
+ "additionalItems",
81
+ "contains",
82
+ "unevaluatedItems",
83
+ "additionalProperties",
84
+ "unevaluatedProperties",
85
+ "propertyNames",
86
+ "not",
87
+ "if",
88
+ "then",
89
+ "else",
90
+ "contentSchema",
91
+ ]);
92
+
93
+ /**
94
+ * Removes unescaped backslashes before underscores in one regex pattern. The
95
+ * CodeBuddy upstream validates tool schemas with RE2, which does not support
96
+ * `\_` escapes — a client schema that serializes `\_` (a common JSON
97
+ * representation of a literal underscore) is rejected with a 400. The donor
98
+ * gateway normalizes this before dispatch; a backslash that escapes a
99
+ * backslash is preserved.
100
+ */
101
+ function normalizeRe2Pattern(value: string): string {
102
+ let out = "";
103
+ for (let i = 0; i < value.length; i++) {
104
+ const char = value[i];
105
+ if (char === "\\" && value[i + 1] === "\\") {
106
+ // Escaped backslash: keep the pair so a following `_` stays escaped.
107
+ out += "\\\\";
108
+ i += 1;
109
+ continue;
110
+ }
111
+ if (char === "\\" && value[i + 1] === "_") {
112
+ // `\_` — the upstream's RE2 rejects it; drop the backslash.
113
+ out += "_";
114
+ i += 1;
115
+ continue;
116
+ }
117
+ out += char;
118
+ }
119
+ return out;
120
+ }
121
+
122
+ /** Normalizes RE2-incompatible underscore escapes in schema pattern positions. */
123
+ function normalizeRe2SchemaPatterns(schema: unknown): void {
124
+ if (!isRecord(schema)) return;
125
+ const pattern = schema["pattern"];
126
+ if (typeof pattern === "string") schema["pattern"] = normalizeRe2Pattern(pattern);
127
+ const patternProperties = schema["patternProperties"];
128
+ if (isRecord(patternProperties)) {
129
+ const normalized: Record<string, unknown> = {};
130
+ for (const [key, value] of Object.entries(patternProperties)) {
131
+ const key2 = normalizeRe2Pattern(key);
132
+ if (key2 in normalized) {
133
+ normalized[key2] = { allOf: [normalized[key2], value] };
134
+ } else {
135
+ normalized[key2] = value;
136
+ }
137
+ }
138
+ schema["patternProperties"] = normalized;
139
+ }
140
+ for (const key of RE2_SCHEMA_MAPS) {
141
+ const map = schema[key];
142
+ if (!isRecord(map)) continue;
143
+ for (const child of Object.values(map)) normalizeRe2SchemaPatterns(child);
144
+ }
145
+ for (const key of RE2_SCHEMA_ARRAYS) {
146
+ const list = schema[key];
147
+ if (!Array.isArray(list)) continue;
148
+ for (const child of list) normalizeRe2SchemaPatterns(child);
149
+ }
150
+ for (const key of RE2_SCHEMA_CHILDREN) {
151
+ normalizeRe2SchemaPatterns(schema[key]);
152
+ }
153
+ }
154
+
155
+ /** JSON-serializes a value to cheaply detect a backslash-before-underscore. */
156
+ function hasUnderscoreEscape(value: unknown): boolean {
157
+ try {
158
+ return JSON.stringify(value).includes("\\_");
159
+ } catch {
160
+ return true;
161
+ }
162
+ }
163
+
164
+ /** Returns the declaration with RE2-safe schema patterns, or the same object. */
165
+ function normalizeRe2Declaration(declaration: Record<string, unknown>): Record<string, unknown> {
166
+ const parameters = declaration["parameters"];
167
+ if (!hasUnderscoreEscape(parameters)) return declaration;
168
+ // Deep-copy only when a rewrite is needed; the shared object must not be
169
+ // mutated for every dispatch.
170
+ const out: Record<string, unknown> = { ...declaration };
171
+ out["parameters"] = structuredClone(parameters);
172
+ normalizeRe2SchemaPatterns(out["parameters"]);
173
+ return out;
174
+ }
175
+
58
176
  /**
59
177
  * Buddy's Chat Completions wire has no `web_search_preview` hosted tool; its
60
178
  * search is an ordinary function named `web_search` with a `{query}` schema.
@@ -102,29 +220,28 @@ export function normalizeBuddyToolNames(payload: Record<string, unknown>): void
102
220
  const name = normalizedToolName(sourceName, index, used);
103
221
  used.add(name);
104
222
  if (!replacements.has(sourceName)) replacements.set(sourceName, name);
105
- if (fn !== undefined)
106
- return {
107
- ...rawTool,
108
- function: {
109
- ...fn,
110
- name,
111
- ...(fn["parameters"] === undefined ? {} : { parameters: completeRequiredSchema(fn["parameters"]) }),
112
- },
223
+ if (fn !== undefined) {
224
+ const fnOut = {
225
+ ...fn,
226
+ name,
227
+ ...(fn["parameters"] === undefined ? {} : { parameters: completeRequiredSchema(fn["parameters"]) }),
113
228
  };
114
- if (custom !== undefined)
115
- return {
116
- ...rawTool,
117
- custom: {
118
- ...custom,
119
- name,
120
- ...(custom["format"] === undefined ? {} : { format: completeRequiredSchema(custom["format"]) }),
121
- },
229
+ return normalizeRe2Declaration(fnOut);
230
+ }
231
+ if (custom !== undefined) {
232
+ const customOut = {
233
+ ...custom,
234
+ name,
235
+ ...(custom["format"] === undefined ? {} : { format: completeRequiredSchema(custom["format"]) }),
122
236
  };
123
- return {
237
+ return { ...rawTool, custom: normalizeRe2Declaration(customOut) };
238
+ }
239
+ const bareOut = {
124
240
  ...rawTool,
125
241
  name,
126
242
  ...(rawTool["parameters"] === undefined ? {} : { parameters: completeRequiredSchema(rawTool["parameters"]) }),
127
243
  };
244
+ return normalizeRe2Declaration(bareOut);
128
245
  });
129
246
  const messages = payload["messages"];
130
247
  if (Array.isArray(messages)) {
@@ -138,6 +255,13 @@ export function normalizeBuddyToolNames(payload: Record<string, unknown>): void
138
255
  }
139
256
  }
140
257
  }
258
+ const responseFormat = payload["response_format"];
259
+ if (isRecord(responseFormat) && hasUnderscoreEscape(responseFormat)) {
260
+ const copied = structuredClone(responseFormat) as Record<string, unknown>;
261
+ const jsonSchema = copied["json_schema"];
262
+ if (isRecord(jsonSchema)) normalizeRe2SchemaPatterns(jsonSchema["schema"]);
263
+ payload["response_format"] = copied;
264
+ }
141
265
  const choice = payload["tool_choice"];
142
266
  if (!isRecord(choice)) return;
143
267
  if (choice["type"] === "allowed_tools" && Array.isArray(choice["tools"])) {
@@ -332,10 +456,10 @@ function buddyTemplatePattern(text: string): RegExp {
332
456
  return new RegExp(`(?<!\\w)${buddyTemplateSource(text)}(?!\\w)`, "i");
333
457
  }
334
458
 
335
- // DeepSeek Harness (DSH) role identities. DSH is the harness behind WorkBuddy;
336
- // a history that still carries its self-description reads to the upstream as a
337
- // foreign channel, so the hardened client rewrites it back to the WorkBuddy
338
- // identity rather than forwarding a competing one.
459
+ // DeepSeek Harness (DSH) role identities. DSH is the harness behind the buddy
460
+ // family; a history that still carries its self-description reads to the
461
+ // upstream as a foreign channel, so the hardened client rewrites it back to
462
+ // the CodeBuddy identity rather than forwarding a competing one.
339
463
  const DSH_CODING_PATTERN = new RegExp(
340
464
  `(?<!\\w)${buddyTemplateSource("You are a coding agent powered by the")}` +
341
465
  `[\\s${BUDDY_ZWSP}]+([^\\r\\n]{1,200}?)[\\s${BUDDY_ZWSP}]+` +
@@ -350,17 +474,17 @@ const DSH_DIRECT_PATTERN = buddyTemplatePattern("You are DeepSeek Harness");
350
474
  const DSH_WEB_GUI_PATTERN = buddyTemplatePattern("DeepSeek Harness Web GUI");
351
475
 
352
476
  /** The identity DSH role prompts are rewritten to; neutral and minimal. */
353
- const DSH_WORKBUDDY_IDENTITY = "You are a helpful assistant";
477
+ const DSH_CODEBUDDY_IDENTITY = "You are a helpful assistant";
354
478
 
355
479
  /**
356
480
  * Rewrites DeepSeek Harness role identities in one message's text back to the
357
- * WorkBuddy identity. Port of the donor gateway's always-on
481
+ * CodeBuddy identity. Port of the donor gateway's always-on
358
482
  * `desensitize_channel_body`, scoped to text the wire actually carries.
359
483
  *
360
484
  * This normalizes a replayed history (or a surviving system turn) that still
361
- * claims the DSH harness, so a WorkBuddy request never advertises a competing
485
+ * claims the DSH harness, so a buddy request never advertises a competing
362
486
  * channel identity upstream. The coding persona keeps the model it named:
363
- * "You are a coding agent powered by the X model" becomes the WorkBuddy
487
+ * "You are a coding agent powered by the X model" becomes the CodeBuddy
364
488
  * identity plus "powered by the X model".
365
489
  */
366
490
  export function scrubBuddyChannelIdentityText(text: string): string {
@@ -368,13 +492,13 @@ export function scrubBuddyChannelIdentityText(text: string): string {
368
492
  const withCodingPersona = text.replace(
369
493
  DSH_CODING_PATTERN,
370
494
  (_match, model: string) =>
371
- `${DSH_WORKBUDDY_IDENTITY}. This conversation is powered by the ${model.trim()} model`,
495
+ `${DSH_CODEBUDDY_IDENTITY}. This conversation is powered by the ${model.trim()} model`,
372
496
  );
373
497
  return withCodingPersona
374
- .replace(DSH_CHANNEL_IDENTITY_PATTERN, DSH_WORKBUDDY_IDENTITY)
375
- .replace(DSH_MINIMAL_PATTERN, DSH_WORKBUDDY_IDENTITY)
376
- .replace(DSH_DIRECT_PATTERN, DSH_WORKBUDDY_IDENTITY)
377
- .replace(DSH_WEB_GUI_PATTERN, "WorkBuddy Web GUI");
498
+ .replace(DSH_CHANNEL_IDENTITY_PATTERN, DSH_CODEBUDDY_IDENTITY)
499
+ .replace(DSH_MINIMAL_PATTERN, DSH_CODEBUDDY_IDENTITY)
500
+ .replace(DSH_DIRECT_PATTERN, DSH_CODEBUDDY_IDENTITY)
501
+ .replace(DSH_WEB_GUI_PATTERN, "CodeBuddy Web GUI");
378
502
  }
379
503
 
380
504
  function scrubBuddyMessageText(message: Record<string, unknown>, field: string): void {
@@ -477,6 +601,10 @@ export function buddyPrePayloadCommon(
477
601
  normalizeBuddyImageUrl(payload);
478
602
  // developer role → system (buddy whitelist rejects developer, 11128)
479
603
  normalizeBuddyRoles(payload);
604
+ // Validate the *caller's* thinking controls before the DeepSeek contract may
605
+ // inject its own `thinking` (an injected `{type:"enabled"}` has no budget and
606
+ // must not be rejected as a malformed client shape).
607
+ validateBuddyThinking(payload);
480
608
  applyOpenAIReasoning(payload, request);
481
609
  applyDeepSeekReasoning(payload);
482
610
  // sol family defaults to high reasoning like reference thinking.go defaultDeepSeekEffort — sol without effort gets 500/invoke error otherwise
@@ -504,8 +632,8 @@ export function buddyPrePayloadCommon(
504
632
  cleanupBuddyOrphanToolPairs(arr);
505
633
  coalesceConsecutiveAssistantMessages(arr);
506
634
  // Normalize a replayed harness history before the blocked-code rewrite
507
- // below: DSH role identities are replaced with the WorkBuddy identity, so
508
- // a WorkBuddy-family request never forwards a competing channel identity.
635
+ // below: DSH role identities are replaced with the CodeBuddy identity, so
636
+ // a buddy-family request never forwards a competing channel identity.
509
637
  scrubBuddyChannelIdentity(arr);
510
638
  // After every merge: a quoted `11128` in any surviving text blocks the
511
639
  // whole request, and the merges above are what produce the final text.
@@ -516,6 +644,34 @@ export function buddyPrePayloadCommon(
516
644
  delete payload["agent_prompt"];
517
645
  }
518
646
 
647
+ /**
648
+ * Validates the reasoning controls the CodeBuddy Chat wire accepts, mirroring
649
+ * the donor gateway's `map_reasoning_controls`. The upstream rejects the whole
650
+ * request for shapes it does not model, so a client that sends an unsupported
651
+ * `thinking` shape is failed here with a 400 that names the field — instead of
652
+ * an opaque upstream error.
653
+ */
654
+ function validateBuddyThinking(payload: Record<string, unknown>): void {
655
+ const thinking = isRecord(payload["thinking"]) ? payload["thinking"] : undefined;
656
+ if (thinking === undefined) return;
657
+ const mode = typeof thinking["type"] === "string" ? (thinking["type"] as string) : "";
658
+ if (mode !== "enabled" && mode !== "adaptive" && mode !== "disabled") {
659
+ throw new GatewayError("invalid_request", 400, `CodeBuddy thinking.type must be enabled, adaptive or disabled (got ${JSON.stringify(mode)}) (request {requestId}). Check the shape of what you sent — then resend 🪞`);
660
+ }
661
+ if (mode === "enabled") {
662
+ const budget = thinking["budget_tokens"];
663
+ if (typeof budget !== "number" || !Number.isInteger(budget) || budget < 1024) {
664
+ throw new GatewayError("invalid_request", 400, "CodeBuddy thinking.budget_tokens must be an integer >= 1024 (request {requestId}). Check the shape of what you sent — then resend 🪞");
665
+ }
666
+ } else if (thinking["budget_tokens"] !== undefined) {
667
+ throw new GatewayError("invalid_request", 400, "CodeBuddy thinking.budget_tokens requires thinking.type=enabled (request {requestId}). Check the shape of what you sent — then resend 🪞");
668
+ }
669
+ const display = thinking["display"];
670
+ if (display !== undefined && display !== null && display !== "summarized") {
671
+ throw new GatewayError("invalid_request", 400, "CodeBuddy thinking.display supports only summarized (request {requestId}). Check the shape of what you sent — then resend 🪞");
672
+ }
673
+ }
674
+
519
675
  function injectSolReasoningDefault(payload: Record<string, unknown>): void {
520
676
  const model = typeof payload["model"] === "string" ? (payload["model"] as string) : "";
521
677
  const isSol = model.toLowerCase().includes("sol");
@@ -1138,7 +1294,7 @@ export interface BuddyMessageFinalizeOptions {
1138
1294
  * The single authoritative buddy message-shape pipeline. Every variant runs
1139
1295
  * the same sequence — coalesce, drop empty turns, guarantee the leading system
1140
1296
  * turn, omit content on tool_calls-only assistant turns, then auto-cache — so
1141
- * the wire shape cannot drift between CodeBuddy intl, CN, and WorkBuddy.
1297
+ * the wire shape cannot drift between CodeBuddy intl and CN.
1142
1298
  */
1143
1299
  export function finalizeBuddyMessages(
1144
1300
  messages: Array<Record<string, unknown>>,
@@ -1,7 +1,7 @@
1
1
  import { asCoercedNumber as sharedAsNumber, asRecord as sharedAsRecord } from "../../../shared/coerce";
2
2
 
3
- // Buddy-family daily check-in (daily login credit claim), shared by all three
4
- // variants (`cb`, `cbcn`, `workbuddy`): same Tencent billing facade, same
3
+ // Buddy-family daily check-in (daily login credit claim), shared by both
4
+ // variants (`cb`, `cbcn`): same Tencent billing facade, same
5
5
  // status/claim contract — only the endpoint base and identity headers differ
6
6
  // per variant, and those already live in each provider's own modules.
7
7
  //
@@ -22,15 +22,8 @@ import { asCoercedNumber as sharedAsNumber, asRecord as sharedAsRecord } from ".
22
22
 
23
23
  import { providerBaseUrl } from "../../provider-metadata";
24
24
  import { sleep } from "../../../shared/sleep";
25
- import { WORKBUDDY_DOMAIN } from "./workbuddy/shared";
26
25
  import { BUDDY_PROVIDER_IDS } from "../../provider-metadata";
27
26
  import { QODER_CHECKIN_PROVIDER_IDS } from "../qoder/qoder-checkin";
28
- import {
29
- buildWorkBuddyUserAgent,
30
- getWorkBuddyClientVersion,
31
- getWorkBuddyCnClientVersion,
32
- getWorkBuddyCliVersion,
33
- } from "../../operations/client-versions";
34
27
  import { buildCodeBuddyUserAgent } from "../../operations/client-versions";
35
28
  import { codebuddyDomain, type CodeBuddyVariant } from "./codebuddy/shared";
36
29
  /** Providers whose billing facade exposes the daily check-in routes. */
@@ -186,8 +179,8 @@ export function buildActivityReportEvent(args: {
186
179
  * chat send. A single event did not move the growth tasks; four events per
187
180
  * batch, sent twice with a pause between the rounds, is what does. The pause
188
181
  * is what keeps the two rounds from landing as one burst. Both numbers apply
189
- * to every buddy variant — the request is built the same way for `cb`,
190
- * `cbcn`, and `workbuddy`, so none of them can drift back to a single shot.
182
+ * to every buddy variant — the request is built the same way for `cb` and
183
+ * `cbcn`, so neither can drift back to a single shot.
191
184
  */
192
185
  export const ACTIVITY_REPORT_EVENTS_PER_BATCH = 4;
193
186
  export const ACTIVITY_REPORT_ROUNDS = 2;
@@ -196,8 +189,8 @@ export const ACTIVITY_REPORT_ROUND_DELAY_MS = 1_500;
196
189
  /**
197
190
  * Sends the growth-activity report (`POST {base}/v2/report`, the versioned
198
191
  * billing-meter path joined the same way as the status/claim routes:
199
- * `VERSION_SEGMENT` supplies `/v2` for the bare `workbuddy` base and nothing
200
- * for `cb`/`cbcn`, which already end in it).
192
+ * `VERSION_SEGMENT` supplies nothing for `cb`/`cbcn`, which already end in
193
+ * `/v2`).
201
194
  *
202
195
  * Two rounds of four events, paused between rounds. Each event carries its
203
196
  * own conversation and request id: the facade drops a repeated id as a
@@ -290,13 +283,10 @@ const CLAIM_RETRY_DELAYS_MS = [1_000, 2_000] as const;
290
283
  /**
291
284
  * Check-in paths are joined onto the provider's manifest base URL, which does
292
285
  * NOT carry a consistent version segment: `cb`/`cbcn` already end in `/v2`,
293
- * while WorkBuddy variants are bare and need `/v2` prepended. This map keeps that
294
- * join aligned with each provider's own quota collector, so the three
295
- * gateways cannot drift apart.
286
+ * so no segment is prepended. This map keeps the join aligned with each
287
+ * provider's own quota collector, so the gateways cannot drift apart.
296
288
  */
297
289
  const VERSION_SEGMENT: Readonly<Record<string, string>> = {
298
- workbuddy: "/v2",
299
- workbuddycn: "/v2",
300
290
  cb: "",
301
291
  cbcn: "",
302
292
  };
@@ -311,10 +301,9 @@ function claimUrl(providerId: string): string {
311
301
 
312
302
  /**
313
303
  * Per-provider billing headers. The Buddy gateways share the Tencent billing
314
- * facade but each risk-gates on its own client identity: WorkBuddy wants the
315
- * desktop brand, CodeBuddy intl wants the `IDE` identity, and CN wants `CLI`
316
- * (plus its own `X-Domain`). Sending the wrong one trips a 403 the same way
317
- * the chat surface does.
304
+ * facade but each risk-gates on its own client identity: CodeBuddy intl
305
+ * wants the `IDE` identity, and CN wants `CLI` (plus its own `X-Domain`).
306
+ * Sending the wrong one trips a 403 the same way the chat surface does.
318
307
  */
319
308
  async function checkinHeaders(providerId: string): Promise<Record<string, string>> {
320
309
  const common = {
@@ -324,24 +313,13 @@ async function checkinHeaders(providerId: string): Promise<Record<string, string
324
313
  "Content-Type": "application/json",
325
314
  Accept: "application/json",
326
315
  };
327
- if (providerId === "cb" || providerId === "cbcn") {
328
- const variant: CodeBuddyVariant = providerId === "cb" ? "IDE" : "CLI";
329
- return {
330
- ...common,
331
- "User-Agent": buildCodeBuddyUserAgent(variant),
332
- "X-IDE-Type": variant,
333
- "X-IDE-Name": variant,
334
- "X-Domain": codebuddyDomain(variant),
335
- };
336
- }
337
- const clientVersion = providerId === "workbuddycn"
338
- ? getWorkBuddyCnClientVersion()
339
- : getWorkBuddyClientVersion();
316
+ const variant: CodeBuddyVariant = providerId === "cb" ? "IDE" : "CLI";
340
317
  return {
341
318
  ...common,
342
- "User-Agent": buildWorkBuddyUserAgent(clientVersion, getWorkBuddyCliVersion()),
343
- "X-Domain": providerId === "workbuddycn" ? "www.workbuddy.cn" : WORKBUDDY_DOMAIN,
344
- "Accept-Language": "en-US",
319
+ "User-Agent": buildCodeBuddyUserAgent(variant),
320
+ "X-IDE-Type": variant,
321
+ "X-IDE-Name": variant,
322
+ "X-Domain": codebuddyDomain(variant),
345
323
  };
346
324
  }
347
325
 
@@ -392,7 +370,7 @@ function envelopeCode(payload: unknown): number | null {
392
370
  }
393
371
 
394
372
  /**
395
- * Unwraps the check-in payload. WorkBuddy returns a flat `{ code, data }`,
373
+ * Unwraps the check-in payload. The gateway returns a flat `{ code, data }`,
396
374
  * while the CodeBuddy billing facade nests Tencent's `data.Response.Data`
397
375
  * (the same double wrap its quota endpoint uses), so both shapes resolve.
398
376
  */
@@ -62,9 +62,9 @@ export interface BuddyConversationIds {
62
62
  * Wire format for the two message-scoped ids (`x-conversation-message-id`,
63
63
  * `x-request-id`).
64
64
  *
65
- * The reference emits these as dashed UUIDs on the WorkBuddy desktop product
65
+ * The reference emits these as dashed UUIDs on the desktop product
66
66
  * and as 32-hex on CLI traffic (`build_attempt_headers` converts only the
67
- * message/request ids, and only for the WorkBuddy profiles). The turn
67
+ * message/request ids, and only for the desktop profiles). The turn
68
68
  * aggregation key (`x-conversation-request-id` / `x-root-request-id`) and the
69
69
  * B3/W3C trace ids stay hex in both styles, because B3's spec accepts only
70
70
  * hex. Default `"hex"`.
@@ -1,10 +1,10 @@
1
1
  import { asNumber as sharedAsNumber, asRecord } from "../../../shared/coerce";
2
2
 
3
3
  /**
4
- * Model discovery for the buddy family (CodeBuddy intl / CN, WorkBuddy).
4
+ * Model discovery for the buddy family (CodeBuddy intl / CN).
5
5
  *
6
- * The three sites are protocol-isomorphic but NOT path-isomorphic, and the
7
- * difference is load-bearing: the international sites answer
6
+ * The two sites are protocol-isomorphic but NOT path-isomorphic, and the
7
+ * difference is load-bearing: the international site answers
8
8
  * `/v2/enterprises/personal/models`, while the CN site answers
9
9
  * `/console/enterprises/personal/models`. Probing the CN path on an
10
10
  * international host returns HTTP 500 from the edge (APISIX) rather than 404 —
@@ -34,8 +34,8 @@ import { buddyAccountUid } from "./buddy-oauth-shared";
34
34
  export const BUDDY_CN_MODELS_PATH = "/console/enterprises/personal/models" as const;
35
35
 
36
36
  /**
37
- * Console directory path on the international sites (`codebuddy.ai`,
38
- * `workbuddy.ai`). The CN path above answers 500 on these hosts.
37
+ * Console directory path on the international site (`codebuddy.ai`). The CN
38
+ * path above answers 500 on these hosts.
39
39
  */
40
40
  export const BUDDY_INTL_MODELS_PATH = "/v2/enterprises/personal/models" as const;
41
41
 
@@ -1,5 +1,6 @@
1
1
  import type { ProviderDispatchContext } from "../../provider-contracts";
2
2
  import { createHash } from "node:crypto";
3
+ import { readFileSync } from "node:fs";
3
4
  import { decodeJwtPayload } from "../../authentication/oauth-flow-store";
4
5
  import { buddyAccountUid } from "./buddy-oauth-shared";
5
6
  import type { CanonicalRequest } from "../../../protocol/canonical-model";
@@ -166,6 +167,14 @@ export function materializeBuddyDeviceBinding(
166
167
  * the operator pinned them in `auth_state` (there is no reliable source to
167
168
  * invent them from). An opaque API key carries no uid and yields nothing.
168
169
  */
170
+ /** A short printable identity claim from a JWT payload, or `undefined`. */
171
+ function identityClaim(claims: Record<string, unknown> | undefined, name: string): string | undefined {
172
+ if (claims === undefined) return undefined;
173
+ const value = claims[name];
174
+ if (typeof value === "string" && value.trim().length > 0 && value.trim().length <= 128) return value.trim();
175
+ return undefined;
176
+ }
177
+
169
178
  export function buddyOAuthIdentityHeaders(
170
179
  context?: ProviderDispatchContext,
171
180
  ): Record<string, string> {
@@ -174,15 +183,20 @@ export function buddyOAuthIdentityHeaders(
174
183
  const uid = buddyAccountUid(token) ?? context?.credential.account_id ?? "";
175
184
  if (uid.length === 0) return {};
176
185
  const enterpriseId = buddyEnterprise(context);
186
+ // The reference gateway reads `id_source` / `token_source` from the JWT
187
+ // payload and the account record, not from stored auth state — an account
188
+ // authorized before either was persisted still carries them on the wire.
189
+ // Precedence matches the donor (`client_profiles._identity_claim_headers`):
190
+ // account fields first, then JWT claims, then legacy `auth_state`.
191
+ const claims = token.length > 0 ? decodeJwtPayload(token) : undefined;
177
192
  const state = context?.credential.auth_state;
178
193
  const idSource =
179
- typeof state?.["idSource"] === "string" && (state["idSource"] as string).trim().length > 0
180
- ? (state["idSource"] as string).trim()
181
- : undefined;
194
+ identityClaim(claims, "id_source") ??
195
+ identityClaim(claims, "idSource") ??
196
+ identityClaim(state as Record<string, unknown> | undefined, "idSource");
182
197
  const method =
183
- typeof state?.["authMethod"] === "string" && (state["authMethod"] as string).trim().length > 0
184
- ? (state["authMethod"] as string).trim()
185
- : undefined;
198
+ identityClaim(claims, "token_source") ??
199
+ identityClaim(state as Record<string, unknown> | undefined, "authMethod");
186
200
  const headers: Record<string, string> = { "X-User-Id": uid };
187
201
  if (method !== undefined) headers["X-Auth-Method"] = method;
188
202
  if (idSource !== undefined) headers["X-Id-Source"] = idSource;
@@ -228,3 +242,72 @@ export function buddyAgentPurpose(
228
242
  if (typeof fromRequest === "string" && fromRequest.trim().length > 0) return fromRequest.trim();
229
243
  return undefined;
230
244
  }
245
+
246
+ /**
247
+ * Desktop risk token, sent only when the account actually has one.
248
+ *
249
+ * The account's own `auth_state` wins. When it carries none, a file the
250
+ * desktop client writes (`CARTETHYIA_DEVICE_TOKEN_FILE`, default
251
+ * `/app/data/device_token`) is read instead — cached for five minutes, and
252
+ * ignored when it is larger than 1 KB, because a token file that size is not
253
+ * a token. A missing file is the normal case and reads as "no token".
254
+ */
255
+ const DEVICE_TOKEN_FILE_MAX_BYTES = 1024;
256
+ const DEVICE_TOKEN_FILE_TTL_MS = 5 * 60_000;
257
+ let deviceTokenFileCache: { readonly value: string | undefined; readonly readAt: number } | undefined;
258
+
259
+ export function readBuddyDeviceToken(context?: ProviderDispatchContext): string | undefined {
260
+ const state = context?.credential.auth_state;
261
+ const value = state?.["deviceToken"] ?? state?.["device_token"];
262
+ if (typeof value === "string" && value.trim().length > 0) return value.trim();
263
+ return readDeviceTokenFile();
264
+ }
265
+
266
+ /**
267
+ * The reference `X-Device-Token` pair, per the official desktop app.
268
+ *
269
+ * The desktop client always presents one of the
270
+ * two on chat traffic — a real token from the TuringShield SDK, or an
271
+ * `X-Device-Token-Error` when the SDK cannot produce one. It never omits both,
272
+ * and it sends the header on every realm (Intl and CN alike).
273
+ *
274
+ * The TuringShield SDK was a proprietary native addon shipped under
275
+ * `vendor/turing-sdk/`; it has been removed. The fallback path below is the
276
+ * captured token (auth_state / `CARTETHYIA_DEVICE_TOKEN_FILE`) and then the
277
+ * official failure state.
278
+ *
279
+ * - Legacy token (auth_state/file) → used when no SDK is available.
280
+ * - CLI identity never carries a device token (the CLI has no device).
281
+ */
282
+ export async function buddyDeviceTokenHeaders(args: {
283
+ realm: "intl" | "cn";
284
+ context: ProviderDispatchContext | undefined;
285
+ cliIdentity: boolean | undefined;
286
+ }): Promise<Record<string, string>> {
287
+ const { context, cliIdentity } = args;
288
+ void args.realm; // the desktop client sends the token on both realms
289
+ if (resolveBuddyIdentity(context, cliIdentity) === "cli") return {};
290
+ const token = readBuddyDeviceToken(context);
291
+ if (token !== undefined) return { "x-device-token": token };
292
+ return { "x-device-token-error": "sdk_not_initialized" };
293
+ }
294
+
295
+ function readDeviceTokenFile(): string | undefined {
296
+ const now = Date.now();
297
+ if (deviceTokenFileCache !== undefined && now - deviceTokenFileCache.readAt < DEVICE_TOKEN_FILE_TTL_MS) {
298
+ return deviceTokenFileCache.value;
299
+ }
300
+ const path = process.env["CARTETHYIA_DEVICE_TOKEN_FILE"] ?? "/app/data/device_token";
301
+ let value: string | undefined;
302
+ try {
303
+ const raw = readFileSync(path);
304
+ if (raw.byteLength > 0 && raw.byteLength <= DEVICE_TOKEN_FILE_MAX_BYTES) {
305
+ const text = raw.toString("utf8").trim();
306
+ if (text.length > 0) value = text;
307
+ }
308
+ } catch {
309
+ value = undefined;
310
+ }
311
+ deviceTokenFileCache = { value, readAt: now };
312
+ return value;
313
+ }
@@ -1,13 +1,12 @@
1
1
  /**
2
2
  * Buddy-family media generation: image and video.
3
3
  *
4
- * CodeBuddy (intl) and WorkBuddy serve media through the same facade, so shared
5
- * request shaping lives here. Their image endpoint can differ by provider;
4
+ * CodeBuddy (intl + CN) serves media through the same facade, so shared
5
+ * request shaping lives here. Their image endpoint can differ by realm;
6
6
  * identity headers come from {@link codebuddyHeaders} keyed on the variant.
7
7
  *
8
- * Two image paths are supported: CodeBuddy can use its native
9
- * `/images/generations` API, while WorkBuddy keeps its chat-completion image
10
- * path. The configured provider path selects the wire format.
8
+ * The image path is CodeBuddy's native `/images/generations` API. The
9
+ * configured provider path selects the wire format.
11
10
  *
12
11
  * - **Video** (`seedance-2.5`) is implemented as an async task: submission
13
12
  * expects a task ID, then the adapter polls a sibling task URL until a clip
@@ -1,5 +1,5 @@
1
1
  /**
2
- * The buddy-family device-login mechanism (`cb`, `cbcn`, `workbuddy`).
2
+ * The buddy-family device-login mechanism (`cb`, `cbcn`).
3
3
  *
4
4
  * All three complete device login against the same plugin auth endpoints: a
5
5
  * state POST, a token poll that answers the pending code `11217` until the user
@@ -64,7 +64,7 @@ export function strictResponseCode(payload: unknown): number | undefined {
64
64
  }
65
65
 
66
66
  /**
67
- * Reads the envelope `code` through `Number()`, which is how the WorkBuddy
67
+ * Reads the envelope `code` through `Number()`, which is how the buddy
68
68
  * gateway is read. Looser than `strictResponseCode` on a malformed payload: a
69
69
  * `null` code coerces to `0`, i.e. success, and `true` to `1`. Kept verbatim
70
70
  * rather than tightened because nothing here shows what that gateway sends for
@@ -2,7 +2,7 @@ import { asNumber as sharedAsNumber } from "../../../shared/coerce";
2
2
 
3
3
  /**
4
4
  * Shared Tencent billing-meter parsing for the CodeBuddy family
5
- * (`cb`, `cbcn`, `workbuddy`).
5
+ * (`cb`, `cbcn`).
6
6
  *
7
7
  * All three read the same endpoint shape — POST
8
8
  * `{base}/v2/billing/meter/get-user-resource` answering
@@ -1,7 +1,5 @@
1
1
  /**
2
- * Real credit deductions for the Tencent "buddy" family (`cb`, `cbcn`,
3
- * `workbuddy`, `workbuddycn`).
4
- *
2
+ * Real credit deductions for the Tencent "buddy" family (`cb`, `cbcn`). *
5
3
  * Every provider in the family reads the same usage-detail endpoint —
6
4
  * `POST {billingHost}/billing/meter/get-user-request-usage` — which answers the
7
5
  * actual per-request deductions the billing meter recorded: when the request