cartethyia 2.7.8 → 2.7.9

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 (371) hide show
  1. package/.env.example +0 -1
  2. package/README.md +29 -59
  3. package/dist/dashboard/assets/BannedPage-BxoG1MrL.js +1 -0
  4. package/dist/dashboard/assets/BannedPage-BxoG1MrL.js.gz +0 -0
  5. package/dist/dashboard/assets/{CliToolDetailPage-BRWKPQPf.js → CliToolDetailPage-ixwgKk53.js} +1 -1
  6. package/dist/dashboard/assets/CliToolDetailPage-ixwgKk53.js.gz +0 -0
  7. package/dist/dashboard/assets/{CliToolsPage-BP-Y5iHN.js → CliToolsPage-BmttNV3U.js} +1 -1
  8. package/dist/dashboard/assets/CliToolsPage-BmttNV3U.js.gz +0 -0
  9. package/dist/dashboard/assets/CombosPage-BE3qwwfC.js +1 -0
  10. package/dist/dashboard/assets/CombosPage-BE3qwwfC.js.gz +0 -0
  11. package/dist/dashboard/assets/ConsoleLogPage-CRZdmMIX.js +1 -0
  12. package/dist/dashboard/assets/ConsoleLogPage-CRZdmMIX.js.gz +0 -0
  13. package/dist/dashboard/assets/CustomizationPage-Dv_FJ6Er.js +1 -0
  14. package/dist/dashboard/assets/CustomizationPage-Dv_FJ6Er.js.gz +0 -0
  15. package/dist/dashboard/assets/HealthEventsModal-CoVrw2sM.js +1 -0
  16. package/dist/dashboard/assets/HealthEventsModal-CoVrw2sM.js.gz +0 -0
  17. package/dist/dashboard/assets/{InjectSystemPromptPanel-Cv-2Ol_K.js → InjectSystemPromptPanel-BOoO8rpz.js} +2 -2
  18. package/dist/dashboard/assets/InjectSystemPromptPanel-BOoO8rpz.js.gz +0 -0
  19. package/dist/dashboard/assets/{LoginPage-bKfz_b1K.js → LoginPage-ChJuNNVN.js} +1 -1
  20. package/dist/dashboard/assets/LoginPage-ChJuNNVN.js.gz +0 -0
  21. package/dist/dashboard/assets/{ModelPicker-B-WQxx_H.js → ModelPicker-BZAJtPg1.js} +1 -1
  22. package/dist/dashboard/assets/ModelPicker-BZAJtPg1.js.gz +0 -0
  23. package/dist/dashboard/assets/OverviewPage-BE40s3u5.js +1 -0
  24. package/dist/dashboard/assets/OverviewPage-BE40s3u5.js.gz +0 -0
  25. package/dist/dashboard/assets/ProviderDetailPage-sfLoQtPV.js +8 -0
  26. package/dist/dashboard/assets/ProviderDetailPage-sfLoQtPV.js.gz +0 -0
  27. package/dist/dashboard/assets/ProviderIcon-CQ6bPUf_.js +1 -0
  28. package/dist/dashboard/assets/ProviderIcon-CQ6bPUf_.js.gz +0 -0
  29. package/dist/dashboard/assets/ProvidersPage-DcepmPWo.js +1 -0
  30. package/dist/dashboard/assets/ProvidersPage-DcepmPWo.js.gz +0 -0
  31. package/dist/dashboard/assets/{ProxyPage-emlTuxz6.js → ProxyPage-BJ87Pp0U.js} +1 -1
  32. package/dist/dashboard/assets/ProxyPage-BJ87Pp0U.js.gz +0 -0
  33. package/dist/dashboard/assets/{QuotaPage-CMJKNYWb.js → QuotaPage-gkSyRAKa.js} +1 -1
  34. package/dist/dashboard/assets/QuotaPage-gkSyRAKa.js.gz +0 -0
  35. package/dist/dashboard/assets/SettingsPage-C5HPWqEv.js +1 -0
  36. package/dist/dashboard/assets/SettingsPage-C5HPWqEv.js.gz +0 -0
  37. package/dist/dashboard/assets/{SetupPage-CUZrKs9r.js → SetupPage-CFNcATJJ.js} +1 -1
  38. package/dist/dashboard/assets/SetupPage-CFNcATJJ.js.gz +0 -0
  39. package/dist/dashboard/assets/{StudioPage-jWmUdDpy.js → StudioPage-YoD4tY5l.js} +1 -1
  40. package/dist/dashboard/assets/StudioPage-YoD4tY5l.js.gz +0 -0
  41. package/dist/dashboard/assets/UsagePage-DxjaAhb_.js +56 -0
  42. package/dist/dashboard/assets/UsagePage-DxjaAhb_.js.gz +0 -0
  43. package/dist/dashboard/assets/api-keys-BTlkq_Uk.js +1 -0
  44. package/dist/dashboard/assets/api-keys-BTlkq_Uk.js.gz +0 -0
  45. package/dist/dashboard/assets/app-BX2nnKqs.css +2 -0
  46. package/dist/dashboard/assets/app-BX2nnKqs.css.gz +0 -0
  47. package/dist/dashboard/assets/assert-CndthXFQ.js +1 -0
  48. package/dist/dashboard/assets/assert-CndthXFQ.js.gz +0 -0
  49. package/dist/dashboard/assets/{badge-DyI_mJfi.js → badge-DdbT_7Am.js} +1 -1
  50. package/dist/dashboard/assets/badge-DdbT_7Am.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-CsGN-nbm.js} +1 -1
  54. package/dist/dashboard/assets/banner-CsGN-nbm.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-mgZ_TLLZ.js} +1 -1
  64. package/dist/dashboard/assets/card-mgZ_TLLZ.js.gz +0 -0
  65. package/dist/dashboard/assets/cli-tools-A1-v8-03.js +1 -0
  66. package/dist/dashboard/assets/cli-tools-A1-v8-03.js.gz +0 -0
  67. package/dist/dashboard/assets/{clipboard-button-ByjCk0o9.js → clipboard-button-CJ4Zty89.js} +1 -1
  68. package/dist/dashboard/assets/clipboard-button-CJ4Zty89.js.gz +0 -0
  69. package/dist/dashboard/assets/{dialog-BZhOlfz-.js → dialog-B5L2jPQ_.js} +1 -1
  70. package/dist/dashboard/assets/dialog-B5L2jPQ_.js.gz +0 -0
  71. package/dist/dashboard/assets/{drawer-BADqIRYP.js → drawer-DkjUouAj.js} +1 -1
  72. package/dist/dashboard/assets/drawer-DkjUouAj.js.gz +0 -0
  73. package/dist/dashboard/assets/{entry-COaC-irN.js → entry-B-LVLV6o.js} +1 -1
  74. package/dist/dashboard/assets/entry-B-LVLV6o.js.gz +0 -0
  75. package/dist/dashboard/assets/entry-CAhdYl3B.js +1 -0
  76. package/dist/dashboard/assets/entry-CAhdYl3B.js.gz +0 -0
  77. package/dist/dashboard/assets/entry-D2xD0Tmd.js +4 -0
  78. package/dist/dashboard/assets/entry-D2xD0Tmd.js.gz +0 -0
  79. package/dist/dashboard/assets/{formatters-DcHJyRiu.js → formatters-CrsdLVsn.js} +1 -1
  80. package/dist/dashboard/assets/formatters-CrsdLVsn.js.gz +0 -0
  81. package/dist/dashboard/assets/{index-CgKmFngv.js → index-BU64Lqvp.js} +2 -2
  82. package/dist/dashboard/assets/index-BU64Lqvp.js.gz +0 -0
  83. package/dist/dashboard/assets/{inline-B2BirKry.js → inline-B2aDLyyg.js} +1 -1
  84. package/dist/dashboard/assets/inline-B2aDLyyg.js.gz +0 -0
  85. package/dist/dashboard/assets/{input-D-qV9dKY.js → input-BCVkdR9d.js} +1 -1
  86. package/dist/dashboard/assets/input-BCVkdR9d.js.gz +0 -0
  87. package/dist/dashboard/assets/{layout-B7tVWhtV.js → layout-DNcAdMK-.js} +1 -1
  88. package/dist/dashboard/assets/layout-DNcAdMK-.js.gz +0 -0
  89. package/dist/dashboard/assets/{list-row-CtkGE3bF.js → list-row-DgrIH0hw.js} +1 -1
  90. package/dist/dashboard/assets/list-row-DgrIH0hw.js.gz +0 -0
  91. package/dist/dashboard/assets/{live-DGizF06-.js → live-Cae01iu8.js} +1 -1
  92. package/dist/dashboard/assets/live-Cae01iu8.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-CRib7H_2.js} +1 -1
  96. package/dist/dashboard/assets/network-CRib7H_2.js.gz +0 -0
  97. package/dist/dashboard/assets/{numbers-BnAd22a_.js → numbers-BKQE8Xpz.js} +1 -1
  98. package/dist/dashboard/assets/numbers-BKQE8Xpz.js.gz +0 -0
  99. package/dist/dashboard/assets/{pagination-BAywyMzS.js → pagination-ecPvG_8g.js} +1 -1
  100. package/dist/dashboard/assets/pagination-ecPvG_8g.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/{query-keys-YSoQOUh6.js → query-keys-IKLh0YFd.js} +1 -1
  106. package/dist/dashboard/assets/query-keys-IKLh0YFd.js.gz +0 -0
  107. package/dist/dashboard/assets/routing-Ckm1UXuC.js +1 -0
  108. package/dist/dashboard/assets/routing-Ckm1UXuC.js.gz +0 -0
  109. package/dist/dashboard/assets/{select-BCOcv8WK.js → select-ChoFWyKz.js} +1 -1
  110. package/dist/dashboard/assets/select-ChoFWyKz.js.gz +0 -0
  111. package/dist/dashboard/assets/settings-CKjFPlKW.js +1 -0
  112. package/dist/dashboard/assets/settings-CKjFPlKW.js.gz +0 -0
  113. package/dist/dashboard/assets/{stack-ByKt3Q1g.js → stack-DWCwU3fh.js} +1 -1
  114. package/dist/dashboard/assets/stack-DWCwU3fh.js.gz +0 -0
  115. package/dist/dashboard/assets/{state-DXbGTx7N.js → state-Cg3VL5J_.js} +1 -1
  116. package/dist/dashboard/assets/state-Cg3VL5J_.js.gz +0 -0
  117. package/dist/dashboard/assets/story/25817331.webp +0 -0
  118. package/dist/dashboard/assets/story/Shorekeeper.webp +0 -0
  119. package/dist/dashboard/assets/story/cartethyia-god.webp +0 -0
  120. package/dist/dashboard/assets/story/fleurdelys_plus.webp +0 -0
  121. package/dist/dashboard/assets/story/jinhsi-blossom.webp +0 -0
  122. package/dist/dashboard/assets/story/phrolova-a.webp +0 -0
  123. package/dist/dashboard/assets/story/requestdeniawokkjpg.webp +0 -0
  124. package/dist/dashboard/assets/story/wuthering-waves-hiyuki-aemeath-hiyuki-rover.webp +0 -0
  125. package/dist/dashboard/assets/{switch-ZeTsW623.js → switch-Db70BMix.js} +1 -1
  126. package/dist/dashboard/assets/switch-Db70BMix.js.gz +0 -0
  127. package/dist/dashboard/assets/{useMutation-CbCP4w1o.js → useMutation-BqdE0LuM.js} +1 -1
  128. package/dist/dashboard/assets/useMutation-BqdE0LuM.js.gz +0 -0
  129. package/dist/dashboard/console-backgrounds.webp +0 -0
  130. package/dist/dashboard/console-backgrounds_initial.webp +0 -0
  131. package/dist/dashboard/donation/qris-risun-marke.webp +0 -0
  132. package/dist/dashboard/index.html +3 -3
  133. package/dist/dashboard/index.html.gz +0 -0
  134. package/dist/dashboard/precompressed-assets.json +57 -55
  135. package/migrations/full-postgres/0044_add_models_warning_metadata.sql +10 -0
  136. package/migrations/full-postgres/0045_migrate_workbuddy_to_codebuddy.sql +156 -0
  137. package/migrations/lite-sqlite3/0044_add_models_warning_metadata.sql +11 -0
  138. package/migrations/lite-sqlite3/0045_migrate_workbuddy_to_codebuddy.sql +156 -0
  139. package/package.json +1 -1
  140. package/src/boot/dependencies.ts +11 -3
  141. package/src/config/cooldowns.ts +1 -1
  142. package/src/config/spec.ts +0 -6
  143. package/src/console/api-keys/routes.ts +11 -4
  144. package/src/console/console-router.ts +7 -0
  145. package/src/console/dashboard-assets.ts +5 -5
  146. package/src/console/domain-registration.ts +27 -0
  147. package/src/console/ip-bans/routes.ts +99 -0
  148. package/src/console/providers/catalog/catalog-projections.ts +5 -2
  149. package/src/console/providers/catalog/contracts.ts +123 -0
  150. package/src/console/providers/catalog/model-operations.ts +122 -10
  151. package/src/console/providers/catalog/provider-operations.ts +116 -8
  152. package/src/console/providers/catalog/public-model-store.ts +0 -0
  153. package/src/console/providers/catalog/routes.ts +64 -4
  154. package/src/console/providers/catalog/store-helpers.ts +30 -1
  155. package/src/console/providers/catalog/store.ts +243 -3
  156. package/src/console/quota/account-quota-global.ts +89 -1
  157. package/src/console/quota/native-identity.ts +2 -0
  158. package/src/console/settings/contracts.ts +18 -0
  159. package/src/console/settings/store.ts +32 -0
  160. package/src/console/system-prompt/contracts.ts +0 -1
  161. package/src/main.ts +1 -0
  162. package/src/observability/event-query.ts +18 -4
  163. package/src/persistence/dialect.ts +12 -0
  164. package/src/persistence/schema.ts +20 -2
  165. package/src/protocol/dsml.ts +1 -1
  166. package/src/protocol/gateway-error.ts +1 -0
  167. package/src/protocol/reasoning.ts +1 -1
  168. package/src/protocol/request/chat.ts +4 -4
  169. package/src/protocol/request/messages.ts +1 -1
  170. package/src/protocol/response/gemini.ts +6 -1
  171. package/src/protocol/sse-decoder.ts +25 -8
  172. package/src/protocol/stream-error-frames.ts +13 -3
  173. package/src/protocol/translation/quirks.ts +1 -1
  174. package/src/protocol/translation/tool-repair.ts +2 -2
  175. package/src/protocol/upstream/send-claude-messages.ts +15 -4
  176. package/src/protocol/upstream-parsing.ts +1 -1
  177. package/src/providers/adapters/compatible-adapter.ts +7 -5
  178. package/src/providers/authentication/oauth-refresh-service.ts +149 -36
  179. package/src/providers/default-registry.ts +68 -3
  180. package/src/providers/discovery/discovery-types.ts +12 -0
  181. package/src/providers/discovery/probe-phases.ts +61 -30
  182. package/src/providers/discovery/probing-service.ts +279 -164
  183. package/src/providers/integrations/antigravity/antigravity-protocol.ts +117 -2
  184. package/src/providers/integrations/antigravity/antigravity.ts +13 -2
  185. package/src/providers/integrations/buddy/buddy-catalog-shared.ts +72 -34
  186. package/src/providers/integrations/buddy/buddy-chat-shared.ts +18 -18
  187. package/src/providers/integrations/buddy/buddy-checkin.ts +17 -39
  188. package/src/providers/integrations/buddy/buddy-conversation-headers.ts +2 -2
  189. package/src/providers/integrations/buddy/buddy-discovery-shared.ts +5 -5
  190. package/src/providers/integrations/buddy/buddy-identity.ts +70 -0
  191. package/src/providers/integrations/buddy/buddy-media.ts +4 -5
  192. package/src/providers/integrations/buddy/buddy-oauth-shared.ts +2 -2
  193. package/src/providers/integrations/buddy/buddy-quota-shared.ts +1 -1
  194. package/src/providers/integrations/buddy/buddy-usage-shared.ts +1 -3
  195. package/src/providers/integrations/buddy/capabilities.ts +4 -41
  196. package/src/providers/integrations/buddy/codebuddy/cn-catalog.ts +2 -1
  197. package/src/providers/integrations/buddy/codebuddy/cn-provider.ts +20 -1
  198. package/src/providers/integrations/buddy/codebuddy/provider.ts +2 -2
  199. package/src/providers/integrations/buddy/codebuddy/shared.ts +6 -10
  200. package/src/providers/integrations/cursor/cursor-catalog.ts +2 -0
  201. package/src/providers/integrations/cursor/cursor-exec.ts +738 -0
  202. package/src/providers/integrations/cursor/cursor-http2.ts +2 -0
  203. package/src/providers/integrations/cursor/cursor-request.ts +9 -8
  204. package/src/providers/integrations/cursor/cursor-shared.ts +19 -1
  205. package/src/providers/integrations/cursor/cursor.ts +50 -30
  206. package/src/providers/integrations/cursor/protocol/tool-canonical.ts +26 -100
  207. package/src/providers/integrations/devin/devin-auth.ts +353 -0
  208. package/src/providers/integrations/devin/devin-catalog.ts +207 -0
  209. package/src/providers/integrations/devin/devin-http.ts +91 -0
  210. package/src/providers/integrations/devin/devin-quota.ts +214 -0
  211. package/src/providers/integrations/devin/devin-shared.ts +111 -0
  212. package/src/providers/integrations/devin/devin.ts +539 -0
  213. package/src/providers/integrations/devin/protocol/devin-proto.ts +584 -0
  214. package/src/providers/integrations/devin/protocol/framing.ts +124 -0
  215. package/src/providers/integrations/factory/factory.ts +2 -2
  216. package/src/providers/integrations/freepi/freepi-catalog.ts +60 -0
  217. package/src/providers/integrations/freepi/freepi-discovery.ts +82 -0
  218. package/src/providers/integrations/freepi/freepi-headers.ts +21 -0
  219. package/src/providers/integrations/freepi/freepi-oauth.ts +142 -0
  220. package/src/providers/integrations/freepi/freepi-protocol.ts +242 -0
  221. package/src/providers/integrations/freepi/freepi-quota.ts +126 -0
  222. package/src/providers/integrations/freepi/freepi-realm.ts +35 -0
  223. package/src/providers/integrations/freepi/freepi.ts +292 -0
  224. package/src/providers/integrations/gemini.ts +2 -2
  225. package/src/providers/integrations/hermes/hermes-oauth.ts +94 -0
  226. package/src/providers/integrations/hermes.ts +4 -12
  227. package/src/providers/integrations/opencode/fingerprint.ts +1 -1
  228. package/src/providers/integrations/perch/perch-catalog.ts +105 -0
  229. package/src/providers/integrations/perch/perch-oauth.ts +170 -0
  230. package/src/providers/integrations/perch/perch-protocol.ts +211 -0
  231. package/src/providers/integrations/perch/perch-quota.ts +137 -0
  232. package/src/providers/integrations/perch/perch-realm.ts +82 -0
  233. package/src/providers/integrations/perch/perch-system-prompt.ts +13 -0
  234. package/src/providers/integrations/perch/perch.ts +315 -0
  235. package/src/providers/integrations/perch/saffron-system-prompt.txt +8 -0
  236. package/src/providers/integrations/qoder/chat-proto.ts +328 -328
  237. package/src/providers/integrations/qoder/claim-qoder-credits.ts +1 -1
  238. package/src/providers/integrations/qoder/qoder-adapter.ts +89 -28
  239. package/src/providers/integrations/qoder/qoder-cache.ts +32 -0
  240. package/src/providers/integrations/qoder/qoder-catalog.ts +6 -1
  241. package/src/providers/integrations/qoder/qoder-checkin.ts +2 -2
  242. package/src/providers/integrations/qoder/qoder-grpc.ts +210 -6
  243. package/src/providers/integrations/qoder/qoder-ide.ts +5 -1
  244. package/src/providers/integrations/qoder/qoder-identity-cache.ts +184 -0
  245. package/src/providers/integrations/qoder/qoder-native-identity-store.ts +83 -18
  246. package/src/providers/integrations/qoder/qoder-realm.ts +9 -6
  247. package/src/providers/integrations/qoder/qoder-umid.ts +109 -342
  248. package/src/providers/integrations/qoder/qoder-virtual.ts +33 -1
  249. package/src/providers/integrations/qoder/qoder.ts +3 -6
  250. package/src/providers/integrations/trae/trae-catalog.ts +9 -14
  251. package/src/providers/integrations/trae/trae-headers.ts +36 -11
  252. package/src/providers/integrations/trae/trae-oauth.ts +14 -17
  253. package/src/providers/integrations/trae/trae-protocol.ts +3 -6
  254. package/src/providers/integrations/trae/trae-quota.ts +103 -21
  255. package/src/providers/integrations/trae/trae-realm.ts +36 -15
  256. package/src/providers/integrations/trae/trae.ts +97 -7
  257. package/src/providers/operations/account-error-classification.ts +66 -1
  258. package/src/providers/operations/account-health-service.ts +192 -26
  259. package/src/providers/operations/account-refresh-status.ts +284 -0
  260. package/src/providers/operations/account-tier.ts +67 -0
  261. package/src/providers/operations/client-versions.ts +37 -99
  262. package/src/providers/operations/cooldown-rules.ts +61 -0
  263. package/src/providers/operations/provider-catalog-seeder.ts +4 -0
  264. package/src/providers/provider-contracts.ts +29 -0
  265. package/src/providers/provider-metadata.ts +59 -4
  266. package/src/providers/provider-model-definition.ts +36 -0
  267. package/src/providers/quota/refresh.ts +21 -0
  268. package/src/security/abuse.ts +71 -0
  269. package/src/transport/dispatch/proxy-request.ts +1 -0
  270. package/src/transport/dispatch/streaming/stream.ts +1 -0
  271. package/src/transport/dispatch/upstream.ts +4 -1
  272. package/src/transport/failure-policy.ts +113 -7
  273. package/src/transport/routing/router.ts +25 -1
  274. package/src/transport/surface/messages/adapter.ts +2 -1
  275. package/src/transport/surface/responses/encode.ts +12 -1
  276. package/src/workers/daily-checkin.ts +1 -1
  277. package/src/workers/quota-refresh-sweep.ts +1 -1
  278. package/src/workers/scheduler.ts +22 -4
  279. package/dist/dashboard/assets/BannedPage-DeIbIiBU.js +0 -1
  280. package/dist/dashboard/assets/BannedPage-DeIbIiBU.js.gz +0 -0
  281. package/dist/dashboard/assets/CliToolDetailPage-BRWKPQPf.js.gz +0 -0
  282. package/dist/dashboard/assets/CliToolsPage-BP-Y5iHN.js.gz +0 -0
  283. package/dist/dashboard/assets/CombosPage-B7JHozVa.js +0 -1
  284. package/dist/dashboard/assets/CombosPage-B7JHozVa.js.gz +0 -0
  285. package/dist/dashboard/assets/ConsoleLogPage-GX7iGGcu.js +0 -1
  286. package/dist/dashboard/assets/ConsoleLogPage-GX7iGGcu.js.gz +0 -0
  287. package/dist/dashboard/assets/CustomizationPage-HVonbGEa.js +0 -1
  288. package/dist/dashboard/assets/CustomizationPage-HVonbGEa.js.gz +0 -0
  289. package/dist/dashboard/assets/HealthEventsModal-xaut4OJ9.js +0 -1
  290. package/dist/dashboard/assets/HealthEventsModal-xaut4OJ9.js.gz +0 -0
  291. package/dist/dashboard/assets/InjectSystemPromptPanel-Cv-2Ol_K.js.gz +0 -0
  292. package/dist/dashboard/assets/LoginPage-bKfz_b1K.js.gz +0 -0
  293. package/dist/dashboard/assets/ModelPicker-B-WQxx_H.js.gz +0 -0
  294. package/dist/dashboard/assets/OverviewPage-Z9pJZFo8.js +0 -1
  295. package/dist/dashboard/assets/OverviewPage-Z9pJZFo8.js.gz +0 -0
  296. package/dist/dashboard/assets/ProviderDetailPage-CMeDBxR1.js +0 -7
  297. package/dist/dashboard/assets/ProviderDetailPage-CMeDBxR1.js.gz +0 -0
  298. package/dist/dashboard/assets/ProviderIcon-jjqXJDA3.js +0 -1
  299. package/dist/dashboard/assets/ProviderIcon-jjqXJDA3.js.gz +0 -0
  300. package/dist/dashboard/assets/ProvidersPage-BXPdOpG4.js +0 -1
  301. package/dist/dashboard/assets/ProvidersPage-BXPdOpG4.js.gz +0 -0
  302. package/dist/dashboard/assets/ProxyPage-emlTuxz6.js.gz +0 -0
  303. package/dist/dashboard/assets/QuotaPage-CMJKNYWb.js.gz +0 -0
  304. package/dist/dashboard/assets/SettingsPage-BSy7EwGo.js +0 -1
  305. package/dist/dashboard/assets/SettingsPage-BSy7EwGo.js.gz +0 -0
  306. package/dist/dashboard/assets/SetupPage-CUZrKs9r.js.gz +0 -0
  307. package/dist/dashboard/assets/StudioPage-jWmUdDpy.js.gz +0 -0
  308. package/dist/dashboard/assets/UsagePage-5F9tGVGG.js +0 -56
  309. package/dist/dashboard/assets/UsagePage-5F9tGVGG.js.gz +0 -0
  310. package/dist/dashboard/assets/api-keys-KvX83mRe.js +0 -1
  311. package/dist/dashboard/assets/api-keys-KvX83mRe.js.gz +0 -0
  312. package/dist/dashboard/assets/app-BEsU8hKs.css +0 -2
  313. package/dist/dashboard/assets/app-BEsU8hKs.css.gz +0 -0
  314. package/dist/dashboard/assets/assert-CidQVoNo.js +0 -1
  315. package/dist/dashboard/assets/assert-CidQVoNo.js.gz +0 -0
  316. package/dist/dashboard/assets/badge-DyI_mJfi.js.gz +0 -0
  317. package/dist/dashboard/assets/banner-B-yXtCqb.js.gz +0 -0
  318. package/dist/dashboard/assets/brand/bansos_page.png +0 -0
  319. package/dist/dashboard/assets/brand/denia_bansos.jpg +0 -0
  320. package/dist/dashboard/assets/brand/favicon.jpg +0 -0
  321. package/dist/dashboard/assets/brand/login_register.jpg +0 -0
  322. package/dist/dashboard/assets/brand/login_register_initial.jpg +0 -0
  323. package/dist/dashboard/assets/brand/og_bansos.jpg +0 -0
  324. package/dist/dashboard/assets/brand/og_image.jpg +0 -0
  325. package/dist/dashboard/assets/brand/sidebar_ico.jpg +0 -0
  326. package/dist/dashboard/assets/card-_HwAEqFF.js.gz +0 -0
  327. package/dist/dashboard/assets/cli-tools-Bwv5-zGZ.js +0 -1
  328. package/dist/dashboard/assets/cli-tools-Bwv5-zGZ.js.gz +0 -0
  329. package/dist/dashboard/assets/clipboard-button-ByjCk0o9.js.gz +0 -0
  330. package/dist/dashboard/assets/dialog-BZhOlfz-.js.gz +0 -0
  331. package/dist/dashboard/assets/drawer-BADqIRYP.js.gz +0 -0
  332. package/dist/dashboard/assets/entry-COaC-irN.js.gz +0 -0
  333. package/dist/dashboard/assets/entry-Ca7LSCet.js +0 -4
  334. package/dist/dashboard/assets/entry-Ca7LSCet.js.gz +0 -0
  335. package/dist/dashboard/assets/entry-PDd27uCd.js +0 -1
  336. package/dist/dashboard/assets/entry-PDd27uCd.js.gz +0 -0
  337. package/dist/dashboard/assets/formatters-DcHJyRiu.js.gz +0 -0
  338. package/dist/dashboard/assets/index-CgKmFngv.js.gz +0 -0
  339. package/dist/dashboard/assets/inline-B2BirKry.js.gz +0 -0
  340. package/dist/dashboard/assets/input-D-qV9dKY.js.gz +0 -0
  341. package/dist/dashboard/assets/layout-B7tVWhtV.js.gz +0 -0
  342. package/dist/dashboard/assets/list-row-CtkGE3bF.js.gz +0 -0
  343. package/dist/dashboard/assets/live-DGizF06-.js.gz +0 -0
  344. package/dist/dashboard/assets/minimize-2-BTmMTK4o.js +0 -1
  345. package/dist/dashboard/assets/minimize-2-BTmMTK4o.js.gz +0 -0
  346. package/dist/dashboard/assets/mutation-GU7XdEjz.js.gz +0 -0
  347. package/dist/dashboard/assets/network-C9UE-bOr.js.gz +0 -0
  348. package/dist/dashboard/assets/numbers-BnAd22a_.js.gz +0 -0
  349. package/dist/dashboard/assets/pagination-BAywyMzS.js.gz +0 -0
  350. package/dist/dashboard/assets/query-keys-YSoQOUh6.js.gz +0 -0
  351. package/dist/dashboard/assets/routing-fnCsxgNO.js +0 -1
  352. package/dist/dashboard/assets/routing-fnCsxgNO.js.gz +0 -0
  353. package/dist/dashboard/assets/select-BCOcv8WK.js.gz +0 -0
  354. package/dist/dashboard/assets/settings-BxslsErn.js +0 -1
  355. package/dist/dashboard/assets/settings-BxslsErn.js.gz +0 -0
  356. package/dist/dashboard/assets/stack-ByKt3Q1g.js.gz +0 -0
  357. package/dist/dashboard/assets/state-DXbGTx7N.js.gz +0 -0
  358. package/dist/dashboard/assets/switch-ZeTsW623.js.gz +0 -0
  359. package/dist/dashboard/assets/useMutation-CbCP4w1o.js.gz +0 -0
  360. package/dist/dashboard/console-backgrounds.jpg +0 -0
  361. package/dist/dashboard/console-backgrounds_initial.jpg +0 -0
  362. package/dist/dashboard/donation/qris-risun-marke.png +0 -0
  363. package/src/providers/integrations/buddy/turing-device-token.ts +0 -262
  364. package/src/providers/integrations/buddy/workbuddy/cn-oauth.ts +0 -30
  365. package/src/providers/integrations/buddy/workbuddy/cn-provider.ts +0 -58
  366. package/src/providers/integrations/buddy/workbuddy/oauth.ts +0 -31
  367. package/src/providers/integrations/buddy/workbuddy/provider.ts +0 -209
  368. package/src/providers/integrations/buddy/workbuddy/quota.ts +0 -161
  369. package/src/providers/integrations/buddy/workbuddy/shared.ts +0 -285
  370. /package/dist/dashboard/assets/{app-CTxoqyYB.js → app-B7kIWFm5.js} +0 -0
  371. /package/dist/dashboard/assets/{app-CTxoqyYB.js.gz → app-B7kIWFm5.js.gz} +0 -0
@@ -8,9 +8,9 @@
8
8
  // X-Device-Type, X-Os-Version (all encodeURIComponent'd)
9
9
  // - User-Agent: TraeClient/TTNet
10
10
  //
11
- // The donor's data-plane implementation and the native Agent transport both
12
- // send x-ide-token alongside Authorization. Version headers remain separate:
13
- // they are not universally required by Agent and commercial requests.
11
+ // The native Agent transport also sends x-ide-token alongside Authorization.
12
+ // Version headers remain separate: they are not universally required by Agent
13
+ // and commercial requests.
14
14
 
15
15
  import { createHash } from "node:crypto";
16
16
  import {
@@ -32,9 +32,9 @@ export interface TraeDeviceIdentity {
32
32
  * Derives the device pair the upstream client sends.
33
33
  *
34
34
  * Both are pure one-way derivations from the account id, so one account always
35
- * presents one device and two accounts never share one — the donor notes this
36
- * is the risk-control correlation surface, so a per-process random id would
37
- * look like many devices behind one account and a shared id would link them.
35
+ * presents one device and two accounts never share one — this is the
36
+ * risk-control correlation surface, so a per-process random id would look like
37
+ * many devices behind one account and a shared id would link them.
38
38
  */
39
39
  export function traeDeviceIdentity(uid: string): TraeDeviceIdentity {
40
40
  const seed = String(uid ?? "").trim() || "anonymous";
@@ -52,9 +52,9 @@ export function traeDeviceIdentity(uid: string): TraeDeviceIdentity {
52
52
  /**
53
53
  * OS string reported as `X-Os-Version`.
54
54
  *
55
- * The donor reads this from the host registry, which leaks the operator's real
56
- * Windows build to upstream. This gateway is not the operator's desktop, so a
57
- * fixed value of the same shape is both more private and more stable.
55
+ * Upstream reads this from the host registry, which would leak the operator's
56
+ * real Windows build. This gateway is not the operator's desktop, so a fixed
57
+ * value of the same shape is both more private and more stable.
58
58
  */
59
59
  export const TRAE_OS_VERSION = "Windows 11 Pro";
60
60
 
@@ -70,6 +70,13 @@ export interface TraeHeaderInput {
70
70
  readonly uid: string;
71
71
  /** Version code sent as `x-ide-version-code`. */
72
72
  readonly versionCode?: string;
73
+ /** Client semver (e.g. `3.6.13`), sent as `X-App-Version`/`x-ide-version`. */
74
+ readonly appVersion?: string;
75
+ /**
76
+ * Traffic class, sent as `request-traffic-type`. Defaults to `chat`;
77
+ * the agent surface overrides to `agent`.
78
+ */
79
+ readonly trafficType?: string;
73
80
  /** True for the streaming chat call, which overrides `Accept`. */
74
81
  readonly stream?: boolean;
75
82
  /** Extra/replacement headers, applied case-insensitively. */
@@ -79,7 +86,15 @@ export interface TraeHeaderInput {
79
86
  /**
80
87
  * Builds the header set for one Trae request.
81
88
  *
82
- * `null` in `extra` deletes the header, matching the donor's override rule.
89
+ * `null` in `extra` deletes the header.
90
+ *
91
+ * Deliberately does NOT send:
92
+ * - `x-ide-token` — the canonical client (Trae 1.107.1 bundle, commit
93
+ * `b6786417`) does not send it on the chat/agent data plane; it is an
94
+ * OAuth/business-host header only. Sending it changed nothing for auth but
95
+ * diverges from the reference wire.
96
+ * - `store-country-code` — not sent on the data plane either. The pay/quota
97
+ * collector adds it explicitly where the commercial surface requires it.
83
98
  */
84
99
  export function buildTraeHeaders(input: TraeHeaderInput): Record<string, string> {
85
100
  const config = TRAE_REALMS[input.realm];
@@ -88,7 +103,6 @@ export function buildTraeHeaders(input: TraeHeaderInput): Record<string, string>
88
103
  "X-App-Id": TRAE_APP_ID,
89
104
  "x-plugin-channel": TRAE_PLUGIN_CHANNEL,
90
105
  "x-ide-version-code": input.versionCode ?? config.versionCode,
91
- "x-ide-token": input.token,
92
106
  Authorization: `Cloud-IDE-JWT ${input.token}`,
93
107
  "Content-Type": "application/json",
94
108
  Accept: "application/json",
@@ -99,6 +113,17 @@ export function buildTraeHeaders(input: TraeHeaderInput): Record<string, string>
99
113
  "X-Device-Brand": encodeURIComponent(TRAE_DEVICE_BRAND),
100
114
  "X-Device-Type": encodeURIComponent("windows"),
101
115
  "X-Os-Version": encodeURIComponent(TRAE_OS_VERSION),
116
+ // Semver + traffic-class headers the 3.6.13 harness sends; verified live
117
+ // (2026-11-06) that they are accepted and change nothing (A/B sweep
118
+ // identical). Only sent when the caller supplies a version.
119
+ ...(input.appVersion === undefined
120
+ ? {}
121
+ : {
122
+ "X-App-Version": input.appVersion,
123
+ "X-App-Version-Code": input.appVersion,
124
+ "x-ide-version": input.appVersion,
125
+ }),
126
+ "request-traffic-type": input.trafficType ?? "chat",
102
127
  };
103
128
 
104
129
  if (input.stream === true) {
@@ -1,7 +1,7 @@
1
1
  // Trae credential login and refresh.
2
2
  //
3
- // Trae has a real browser authorization flow, just not a device-code one: the
4
- // other donor's `start_login` answers `unsupported` for device authorization,
3
+ // Trae has a real browser authorization flow, just not a device-code one: an
4
+ // earlier probe of the device-authorization surface answered `unsupported`,
5
5
  // which is what led to the earlier claim that Trae had no OAuth login at all.
6
6
  // The browser path is `GetLoginGuidance` (discovers the login host) → the
7
7
  // operator signs in at `/authorization` → the callback carries a refresh token
@@ -18,9 +18,6 @@
18
18
  // NEW refresh token and invalidates the one sent. A refresh that persisted only
19
19
  // the access token would leave the account holding a dead refresh token and
20
20
  // unrecoverable on its next expiry.
21
- //
22
- // Sourced from the donor hub `Public/trae2api-hub` (`_refresh_account_request`
23
- // in `trae_client.py`).
24
21
 
25
22
  import { randomUUID } from "node:crypto";
26
23
  import { firstNonEmptyString as firstString } from "../../../shared/coerce";
@@ -50,10 +47,9 @@ const TRAE_OAUTH_USER_AGENT = "Trae/1.0.0 antigravity-cockpit-tools";
50
47
  /**
51
48
  * Plugin version the desktop client reports.
52
49
  *
53
- * Observed in a real authorize URL as `2.3.93439`. The donor sent `local`,
54
- * which is what the *web* build reports; the login page accepts it, but the
55
- * value is part of the fingerprint upstream correlates, so the desktop shape
56
- * is used here.
50
+ * Observed in a real authorize URL as `2.3.93439`. The web build reports
51
+ * `local`; the login page accepts it, but the value is part of the fingerprint
52
+ * upstream correlates, so the desktop shape is used here.
57
53
  */
58
54
  const TRAE_PLUGIN_VERSION = "2.3.93439";
59
55
 
@@ -61,9 +57,9 @@ const TRAE_PLUGIN_VERSION = "2.3.93439";
61
57
  * Device context for the login.
62
58
  *
63
59
  * Derived with the same function the data plane uses, so one account presents
64
- * one device across login and requests. A per-process random pair — what the
65
- * donor sends — made every gateway restart look like a brand new device to the
66
- * risk-control surface that correlates them.
60
+ * one device across login and requests. A per-process random pair would make
61
+ * every gateway restart look like a brand new device to the risk-control
62
+ * surface that correlates them.
67
63
  *
68
64
  * The seed is the login trace id because no account identity exists yet: the
69
65
  * authorize step is what produces the account.
@@ -102,10 +98,11 @@ interface TraeExchangeResponse extends TraeExchangeResult {
102
98
 
103
99
  export class TraeOAuthClient extends OAuthClient {
104
100
  /**
105
- * Trae has no device-code flow — the donor's `start_login` answers
106
- * `unsupported` for it — but it does have a browser authorization flow:
107
- * `GetLoginGuidance` discovers the login host, the operator signs in there,
108
- * and the callback carries a refresh token that `ExchangeToken` redeems.
101
+ * Trae has no device-code flow — an earlier probe of the device-authorization
102
+ * surface answered `unsupported` for it — but it does have a browser
103
+ * authorization flow: `GetLoginGuidance` discovers the login host, the
104
+ * operator signs in there, and the callback carries a refresh token that
105
+ * `ExchangeToken` redeems.
109
106
  *
110
107
  * Declaring only the browser half is what keeps the console from offering a
111
108
  * device login that cannot work.
@@ -691,7 +688,7 @@ interface TraeCallbackPayload {
691
688
  * Upstream has two shapes and the page picks by how the login started:
692
689
  *
693
690
  * - `?isRedirect=true&refreshToken=…&refreshExpireAt=…` — the refresh-token
694
- * flow, which is what the donor implements.
691
+ * flow.
695
692
  * - `?isRedirect=true&scope=trae&authCodeInfo={"AuthCode":…}&host=…` — the PKCE
696
693
  * flow, which is what the live page actually produces here. The code inside
697
694
  * `authCodeInfo` is what `public/oauth/ExchangeToken` redeems against the
@@ -8,9 +8,6 @@
8
8
  // 2. Message `content` must be an array of parts. A plain string is refused
9
9
  // (400/4001), so every message is normalized to parts even when there is
10
10
  // only text.
11
- //
12
- // Sourced from the donor hub `Public/trae2api-hub` (`build_chat_body` and
13
- // `iter_events` in `trae_client.py`).
14
11
 
15
12
  import type { CanonicalRequest, ContentPart } from "../../../protocol/canonical-model";
16
13
  import { resolveWireMaxTokens } from "../../../protocol/translation/quirks";
@@ -79,9 +76,9 @@ export interface TraeToolCall {
79
76
  /**
80
77
  * Generation fields upstream accepts.
81
78
  *
82
- * Undeclared fields are deliberately omitted: the donor notes that sending
83
- * anything outside this set risks a 4001, so `provider_options` is never
84
- * spread into the body wholesale.
79
+ * Undeclared fields are deliberately omitted: sending anything outside this
80
+ * set risks a 4001, so `provider_options` is never spread into the body
81
+ * wholesale.
85
82
  */
86
83
  const PASSTHROUGH_OPTIONS = ["temperature", "top_p", "max_tokens", "stop"] as const;
87
84
 
@@ -9,11 +9,11 @@
9
9
  // - `ide_user_ent_usage` on the **pay** host is the entitlement: which package
10
10
  // the account holds and when it lapses.
11
11
  //
12
- // The donor reads both and lets either fail without the other, and that
13
- // tolerance is load-bearing here too: entitlement is a *pay* host, which is the
14
- // first thing a region-blocked or unpaid account loses. Letting a dead pay host
15
- // fail the whole read would report "no quota" for an account whose session
16
- // meter is perfectly readable — and "no quota" is what the router parks on.
12
+ // Both are read and either may fail without the other: entitlement is a *pay*
13
+ // host, which is the first thing a region-blocked or unpaid account loses.
14
+ // Letting a dead pay host fail the whole read would report "no quota" for an
15
+ // account whose session meter is perfectly readable — and "no quota" is what
16
+ // the router parks on.
17
17
  //
18
18
  // Headers come from `buildTraeHeaders` rather than being rebuilt here: the
19
19
  // device fingerprint is derived from the uid, and an account that reports one
@@ -39,14 +39,16 @@ import { buildTraeHeaders } from "./trae-headers";
39
39
  import { log } from "../../../observability/logger";
40
40
  import {
41
41
  TRAE_ENT_USAGE_PATH,
42
+ TRAE_ENT_USAGE_PATH_V2,
42
43
  TRAE_REGION_CORE_HOSTS,
44
+ TRAE_REGION_PAY_HOSTS,
43
45
  TRAE_REALMS,
44
46
  TRAE_SESSION_USAGE_PATH,
45
47
  type TraeRealm,
46
48
  } from "./trae-realm";
47
49
 
48
50
  /**
49
- * The buckets `remain_usage` carries, in the order the donor reads them.
51
+ * The buckets `remain_usage` carries, in the order they are read.
50
52
  *
51
53
  * `advanced_model` and `premium_model_*` are request counts; `dollar_basic` and
52
54
  * `dollar_bonus` are spend balances. They are separate budgets and must never
@@ -210,30 +212,96 @@ function parseSessionUsage(payload: unknown): {
210
212
  return { windows, plan };
211
213
  }
212
214
 
213
- /** The entitlement package name and lapse, from the pay host. */
215
+ /**
216
+ * The entitlement payload, from the pay host.
217
+ *
218
+ * Shape verified live (2026-11-06, intl Free plan):
219
+ *
220
+ * {
221
+ * "billing_version": 3,
222
+ * "is_dollar_usage_billing": true,
223
+ * "user_entitlement_pack_list": [{
224
+ * "display_desc": "Free plan",
225
+ * "entitlement_base_info": {
226
+ * "end_time": 1796083199,
227
+ * "quota": { "advanced_model_request_limit": 1000, ... }
228
+ * },
229
+ * "quota": { "advanced_model_request_limit": 1000, ... },
230
+ * "usage": { "basic_usage_amount": 0, ... },
231
+ * "status": 1,
232
+ * "expire_time": 0
233
+ * }]
234
+ * }
235
+ *
236
+ * The v2 surface (CN realm) instead carries a `usage_summary` block:
237
+ *
238
+ * { "total_amount": 900, "consumed_amount": 0.39, ... }
239
+ *
240
+ * Limits live in `entitlement_base_info.quota` (or the sibling `quota`)
241
+ * as `<bucket>_limit` fields. There is no consumed/remaining count in the
242
+ * v1 shape — `used` stays null, and the window reports just the cap.
243
+ */
214
244
  function parseEntitlement(payload: unknown): {
215
245
  readonly windows: readonly ProviderQuotaWindow[];
216
246
  readonly plan: string | null;
217
247
  } {
218
248
  const root = record(payload) ?? {};
219
249
  const data = record(root["data"]) ?? root;
220
- const items = Array.isArray(data["entitlement_list"])
221
- ? data["entitlement_list"]
222
- : Array.isArray(data["list"])
223
- ? data["list"]
250
+ const packs = Array.isArray(data["user_entitlement_pack_list"])
251
+ ? data["user_entitlement_pack_list"]
252
+ : Array.isArray(data["entitlement_list"])
253
+ ? data["entitlement_list"]
224
254
  : [];
225
255
  const windows: ProviderQuotaWindow[] = [];
226
256
  let plan: string | null = null;
227
- for (const entry of items) {
228
- const item = record(entry);
229
- if (!item) continue;
230
- const name = text(item["ent_title"] ?? item["name"] ?? item["package_name"]);
257
+
258
+ // The pack's `quota` block names its fields `<bucket>_limit` — with the
259
+ // word `request` inside for the model buckets (`advanced_model_request_limit`),
260
+ // and `usage_limit` for the dollar buckets (`basic_usage_limit`). Mapping
261
+ // them explicitly beats string-concatenating the bucket id.
262
+ const LIMIT_FIELDS: Readonly<Record<string, string>> = {
263
+ advanced_model: "advanced_model_request_limit",
264
+ premium_model_fast_request: "premium_model_fast_request_limit",
265
+ premium_model_slow_request: "premium_model_slow_request_limit",
266
+ dollar_basic: "basic_usage_limit",
267
+ dollar_bonus: "bonus_usage_limit",
268
+ };
269
+ const LABELS = new Map<string, string>([...REQUEST_BUCKETS, ...DOLLAR_BUCKETS]);
270
+
271
+ for (const entry of packs) {
272
+ const pack = record(entry);
273
+ if (!pack) continue;
274
+ const base = record(pack["entitlement_base_info"]) ?? {};
275
+ const quota = record(base["quota"]) ?? record(pack["quota"]) ?? {};
276
+ const name = text(pack["display_desc"]) ?? text(pack["name"]) ?? text(pack["title"]);
231
277
  if (!plan && name) plan = name;
232
- const total = number(item["total"] ?? item["quota"] ?? item["advanced_model_request_limit"]);
233
- const remaining = number(item["remain"] ?? item["remaining"] ?? item["remain_usage"]);
234
- const resetsAt = isoDate(item["end_time"] ?? item["endTime"] ?? item["expires_at"]);
235
- const window = bucketWindow(name ?? "Entitlement", remaining, total, resetsAt);
236
- if (window) windows.push(window);
278
+ const resetsAt = isoDate(
279
+ number(base["end_time"]) ?? number(pack["end_time"]) ?? number(pack["expire_time"]),
280
+ );
281
+
282
+ // v2 surface (CN realm): the summary carries the total and consumed
283
+ // amounts; use it as a single window when present.
284
+ const summary = record(record(data["usage_summary"]) ?? record(pack["usage_summary"]));
285
+ if (summary) {
286
+ const window = bucketWindow(
287
+ name ?? "Plan",
288
+ number(summary["consumed_amount"]),
289
+ number(summary["total_amount"]),
290
+ resetsAt,
291
+ );
292
+ if (window) windows.push(window);
293
+ continue;
294
+ }
295
+
296
+ // v1 surface: `<bucket>_limit` fields, no consumption count — the cap
297
+ // is all this shape can report.
298
+ for (const [key, limitField] of Object.entries(LIMIT_FIELDS)) {
299
+ const limit = number(quota[limitField]);
300
+ if (limit === null) continue;
301
+ const label = LABELS.get(key) ?? key;
302
+ const window = percentWindow(`trae:${key}`, label, null, resetsAt, null, limit, "independent");
303
+ if (window) windows.push(window);
304
+ }
237
305
  }
238
306
  return { windows, plan };
239
307
  }
@@ -268,6 +336,17 @@ function coreHost(region: string, config: (typeof TRAE_REALMS)[TraeRealm]): stri
268
336
  return (region !== "" ? TRAE_REGION_CORE_HOSTS[region] : undefined) ?? config.core;
269
337
  }
270
338
 
339
+ /**
340
+ * The pay host for this account.
341
+ *
342
+ * The pay surface correlates the credential against its region, too: an `sg`
343
+ * account sent to the US pay host is answered 401 code 1001, while the SG pay
344
+ * host accepts the same token (verified live 2026-11-06).
345
+ */
346
+ function payHost(region: string, config: (typeof TRAE_REALMS)[TraeRealm]): string {
347
+ return (region !== "" ? TRAE_REGION_PAY_HOSTS[region] : undefined) ?? config.pay;
348
+ }
349
+
271
350
  export async function fetchTraeQuota(
272
351
  credential: string,
273
352
  fetcher: FetchLike,
@@ -349,7 +428,10 @@ export async function fetchTraeQuota(
349
428
  };
350
429
 
351
430
  const session = await tryRealms((config) => coreHost(region, config) + TRAE_SESSION_USAGE_PATH, "session usage", parseSessionUsage);
352
- const entitlement = await tryRealms((config) => config.pay + TRAE_ENT_USAGE_PATH, "entitlement", parseEntitlement);
431
+ // The entitlement path differs by realm: the CN realm answers v2 (v1 lacks
432
+ // `usage_summary` there), the intl realm answers v1 (v2 is a 404).
433
+ const entPath = realm === "cn" ? TRAE_ENT_USAGE_PATH_V2 : TRAE_ENT_USAGE_PATH;
434
+ const entitlement = await tryRealms((config) => payHost(region, config) + entPath, "entitlement", parseEntitlement);
353
435
 
354
436
  const windows = [...(session?.windows ?? []), ...(entitlement?.windows ?? [])];
355
437
  if (!session && !entitlement) {
@@ -1,17 +1,13 @@
1
- // Trae realms: one vendor, two unrelated deployments.
2
- //
3
- // `trae` (global / intl) and `traecn` (China) share the wire protocol — an
4
- // `x-ide-token` credential, a `model_name` body, an SSE stream — but nothing
5
- // else about the address: different core host, different account/oauth host,
6
- // different UA, a *different authorization scheme*, and a different model
7
- // roster. Treating them as one provider with a region flag is what would send
8
- // a CN account to `*.traeapi.us` and get it refused.
1
+ // Regions are per-realm constants: `trae` (global / intl) and `traecn`
2
+ // (China) share the wire protocol — an `x-ide-token` credential, a
3
+ // `model_name` body, an SSE stream — but nothing else about the address:
4
+ // different core host, different account/oauth host, different UA, a
5
+ // *different authorization scheme*, and a different model roster. Treating
6
+ // them as one provider with a region flag is what would send a CN account to
7
+ // `*.traeapi.us` and get it refused.
9
8
  //
10
9
  // Everything per-realm lives here so the adapter, the auth builder, and the
11
10
  // discovery fetcher cannot drift apart on host, UA, or version code.
12
- //
13
- // Sourced from the donor hub `Public/trae2api-hub` (`REALMS` in
14
- // `trae_client.py`, `TRAEREALMS`-shaped config in `trae_accounts.py`).
15
11
 
16
12
  /** One Trae deployment. */
17
13
  export type TraeRealm = "intl" | "cn";
@@ -43,7 +39,7 @@ export interface TraeRealmConfig {
43
39
  /**
44
40
  * Chat path, identical across realms.
45
41
  *
46
- * The list endpoint takes a body (`{"type":"chat"}`); the donor posts it, so
42
+ * The list endpoint takes a body (`{"type":"chat"}`); upstream posts it, so
47
43
  * this is not a bare GET.
48
44
  */
49
45
  export const TRAE_CHAT_PATH = "/api/ide/v1/llm_raw_chat";
@@ -101,8 +97,8 @@ export const TRAE_AUTHORIZATION_PATH = "/authorization";
101
97
  * `/`, a trailing slash — is answered with "Login Failed" before the operator
102
98
  * gets to sign in, so the gateway's own `/callback` default cannot be used.
103
99
  *
104
- * The donor serves this on its own dynamic port; here the port comes from the
105
- * gateway's redirect setting and only the path is fixed.
100
+ * This gateway serves this on its own configured port and only the path is
101
+ * fixed.
106
102
  */
107
103
  export const TRAE_CALLBACK_PATH = "/authorize";
108
104
 
@@ -123,6 +119,14 @@ export const TRAE_LOGIN_GUIDANCE_URLS: readonly string[] = [
123
119
  /** Usage / entitlement paths, relative to the realm's `pay` host. */
124
120
  export const TRAE_SESSION_USAGE_PATH = "/api/v1/commercial/get_session_usage";
125
121
  export const TRAE_ENT_USAGE_PATH = "/trae/api/v1/pay/ide_user_ent_usage";
122
+ /**
123
+ * The v2 entitlement path, used by the CN realm.
124
+ *
125
+ * Verified live (2026-11-06): the CN realm answers v2 — v1 lacks the
126
+ * `usage_summary` block there — while the intl realm answers v1 (v2 is a
127
+ * 404 on the intl pay host).
128
+ */
129
+ export const TRAE_ENT_USAGE_PATH_V2 = "/trae/api/v2/pay/ide_user_ent_usage";
126
130
 
127
131
  /**
128
132
  * Realm → deployment.
@@ -169,7 +173,10 @@ export const TRAE_REALMS: Readonly<Record<TraeRealm, TraeRealmConfig>> = {
169
173
  coreFallbacks: ["https://core-normal.traeapi.us", "https://core-normal.trae.ai"],
170
174
  account: "https://grow-normal.trae.ai",
171
175
  accountFallbacks: ["https://grow-normal.traeapi.us"],
172
- pay: "https://grow-normal.traeapi.us",
176
+ // SG default, like the core: the pay surface is region-correlated too, and
177
+ // the SG host answers an sg-provisioned (or region-unrecorded intl) account
178
+ // that the US host refuses with 401 code 1001 (verified live 2026-11-06).
179
+ pay: "https://grow-normal.trae.ai",
173
180
  ug: "https://grow-normal.traeapi.us",
174
181
  userAgent: "Trae/3.6.2",
175
182
  versionCode: "20260212",
@@ -196,6 +203,20 @@ export const TRAE_REGION_CORE_HOSTS: Readonly<Record<string, string>> = {
196
203
  us: "https://core-normal.traeapi.us",
197
204
  };
198
205
 
206
+ /**
207
+ * Pay host per account region, when the login reported one.
208
+ *
209
+ * Verified live (2026-11-06): an `sg` account's token is refused by the US
210
+ * pay host with `401 code 1001` ("we are not able to authenticate you"), and
211
+ * accepted by the SG pay host. Same region-routing rule as the core host —
212
+ * the pay surface correlates the credential against its region, so a
213
+ * provisioned region must pick its own pay host too.
214
+ */
215
+ export const TRAE_REGION_PAY_HOSTS: Readonly<Record<string, string>> = {
216
+ sg: "https://grow-normal.trae.ai",
217
+ us: "https://grow-normal.traeapi.us",
218
+ };
219
+
199
220
  /** The app id is constant across realms. */
200
221
  export const TRAE_APP_ID = "6eefa01c-1036-4c7e-9ca5-d891f63bfcd8";
201
222
 
@@ -22,6 +22,7 @@ import { resolvePromptCacheKey } from "../../operations/session-resolution";
22
22
  import { estimateTraeSessionCache } from "./trae-cache";
23
23
  import { TRAE_CN_MODELS, TRAE_INTL_MODELS, traeMaxContextWindow } from "./trae-catalog";
24
24
  import { buildTraeHeaders } from "./trae-headers";
25
+ import { getTraeVersion } from "../../operations/client-versions";
25
26
  import {
26
27
  buildTraeChatBody,
27
28
  isTraeAgentEndpoint,
@@ -30,7 +31,7 @@ import {
30
31
  TRAE_AGENT_ENDPOINT,
31
32
  TRAE_CHAT_ENDPOINT,
32
33
  } from "./trae-protocol";
33
- import { TRAE_REGION_CORE_HOSTS, traeCoreCandidates, type TraeRealm } from "./trae-realm";
34
+ import { TRAE_REGION_CORE_HOSTS, traeCoreCandidates, TRAE_MODEL_LIST_PATH, TRAE_AGENT_FUNCTION, type TraeRealm } from "./trae-realm";
34
35
 
35
36
  export interface TraeAdapterOptions {
36
37
  readonly providerId?: string;
@@ -94,13 +95,22 @@ function extractToken(secret: string): string | undefined {
94
95
  * Maps a Trae upstream error (a named `error` SSE event, or an error object
95
96
  * in a frame body) to the canonical GatewayError.
96
97
  *
97
- * The mapping mirrors the reference bridge's `TRAE_ERROR_MAP`:
98
+ * Codes resolved from live probing (2026-11-06), not guessed:
98
99
  * - `1001` — "we are not able to authenticate you" — is a credential verdict.
99
100
  * It must surface as account-scoped `authentication_failed` so the account
100
101
  * is parked for re-login and failover tries a sibling — never the proxy
101
- * pool.
102
+ * pool. Note: the pay host answers it on *region mismatch* too (an sg
103
+ * account sent to the us pay host), which is not the same as an expired
104
+ * token — a token that answers `GetUserInfo` 200 is alive.
102
105
  * - `4008` / "exceeded the quota" — free-tier quota exhausted. That is a
103
106
  * `quota_exceeded` 429, not a platform fault.
107
+ * - `1005` (extra `{"plan":N}`) and `4015` — entitlement verdicts. Verified
108
+ * live: identical wire, a paid-capable account streams the same model fine
109
+ * while a Free account is refused with these codes, and the refused
110
+ * account's token still answers `GetUserInfo` 200. Neither auth nor
111
+ * platform fault: `quota_exceeded`.
112
+ * - `4013` / region message — durable regional unavailability, scoped by the
113
+ * classifier to the account (`trae-region-unavailable`).
104
114
  * - Everything else is a platform fault (`platform_unavailable`).
105
115
  */
106
116
  function throwTraeUpstreamError(
@@ -163,6 +173,27 @@ function throwTraeUpstreamError(
163
173
  );
164
174
  }
165
175
 
176
+ // `1005` and `4015` are entitlement verdicts, not auth failures and not
177
+ // platform faults. Verified live: the same wire streams fine on a
178
+ // paid-capable account while a Free account is refused with these codes,
179
+ // and the refused account's token still answers `GetUserInfo` 200. Treating
180
+ // them as auth would park the account for re-login pointlessly; treating
181
+ // them as platform faults would failover to another account that hits the
182
+ // same wall. They are `quota_exceeded` — the model/account cannot serve.
183
+ if (code === "1005" || code === "4015") {
184
+ throw new GatewayError(
185
+ "quota_exceeded",
186
+ 429,
187
+ `Trae model not available on this account (upstream code ${code}): ${message} (request {requestId}). The model needs a plan or permission this account does not hold — pick another model or account ⏳`,
188
+ {
189
+ providerCode: code,
190
+ providerStatus: code === "1005" ? 1005 : 4015,
191
+ ...(providerId ? { providerId } : {}),
192
+ },
193
+ "upstream",
194
+ );
195
+ }
196
+
166
197
  throw new GatewayError(
167
198
  "platform_unavailable",
168
199
  502,
@@ -234,6 +265,8 @@ class TraeAdapter implements ProviderAdapter {
234
265
  token: auth.token,
235
266
  uid: auth.uid,
236
267
  stream: true,
268
+ appVersion: getTraeVersion(),
269
+ trafficType: agentSurface ? "agent" : "chat",
237
270
  });
238
271
 
239
272
  const primary = traeCoreForAccount(this.realm, context.credential.auth_state);
@@ -326,7 +359,7 @@ class TraeAdapter implements ProviderAdapter {
326
359
  /**
327
360
  * Two frame layouts reach here and both are live.
328
361
  *
329
- * The donor's client nests the answer under `chunk`; the region host this
362
+ * Upstream's client nests the answer under `chunk`; the region host this
330
363
  * gateway talks to sends it flat — `event: output` with
331
364
  * `{"response":"…"}` — and reports usage as a separate `token_usage`
332
365
  * frame with the OpenAI spelling at the top level. Reading only `chunk`
@@ -503,6 +536,17 @@ export function createTraeCnAdapter(options: TraeAdapterOptions = {}): ProviderA
503
536
  /**
504
537
  * Live model discovery.
505
538
  *
539
+ * The source follows the realm, not one endpoint:
540
+ *
541
+ * - **intl** — the agent picker (`batch_get_detail_param` with
542
+ * `function: solo_agent`), filtered to `usage == "chat_completion"` rows.
543
+ * The chat `model_list` is NOT the intl source: it lists only the legacy
544
+ * nine chat ids, and an agent roster row (`gpt-5-paygo`,
545
+ * `deepseek-v4.1-flash`, `search_agent_*`, …) is absent from it (verified
546
+ * live 2026-11-06). Reading it would replace the agent catalog with models
547
+ * the agent surface rejects (`4023 model is unknown`).
548
+ * - **CN** — the chat `model_list`, which is the surface that serves CN.
549
+ *
506
550
  * The listing is per account region, not per realm: it answers on CN and on the
507
551
  * intl region host that serves the account (`coresg-normal.trae.ai` verified),
508
552
  * while the generic intl core answers `403 no schema found`. An empty result is
@@ -526,8 +570,54 @@ export async function discoverTraeModels(input: {
526
570
  realm: input.realm,
527
571
  token: input.token,
528
572
  uid: input.uid,
573
+ appVersion: getTraeVersion(),
529
574
  });
530
- const url = `${traeCoreForAccount(input.realm, input.region === undefined ? undefined : { region: input.region })}/api/ide/v1/model_list`;
575
+ const core = traeCoreForAccount(
576
+ input.realm,
577
+ input.region === undefined ? undefined : { region: input.region },
578
+ );
579
+
580
+ if (input.realm === "intl") {
581
+ // The agent picker: the only surface that lists the intl roster's configs.
582
+ const url = `${core}/api/ide/v1/batch_get_detail_param`;
583
+ const response = await input.fetcher(url, {
584
+ method: "POST",
585
+ headers,
586
+ body: JSON.stringify({ functions: [TRAE_AGENT_FUNCTION], Request: {} }),
587
+ ...(input.signal ? { signal: input.signal } : {}),
588
+ });
589
+ if (!response.ok) return [];
590
+ const parsed = (await response.json()) as { function_configs?: unknown };
591
+ const groups = Array.isArray(parsed.function_configs) ? parsed.function_configs : [];
592
+ const out: { id: string; ctx: number | undefined }[] = [];
593
+ for (const group of groups) {
594
+ if (typeof group !== "object" || group === null) continue;
595
+ const list = (group as Record<string, unknown>)["config_info_list"];
596
+ if (!Array.isArray(list)) continue;
597
+ for (const entry of list) {
598
+ if (typeof entry !== "object" || entry === null) continue;
599
+ const row = entry as Record<string, unknown>;
600
+ // Only chat-capable configs are dispatch targets; fast_apply,
601
+ // title_generation, etc. are not.
602
+ if (String(row["usage"] ?? "") !== "chat_completion") continue;
603
+ const id = String(row["config_name"] ?? "").trim();
604
+ if (id.length === 0) continue;
605
+ // Context: the picker row carries `model_detail_list` with the real
606
+ // upstream rows; prefer the widest selectable window among them.
607
+ let ctx: number | undefined;
608
+ const details = Array.isArray(row["model_detail_list"]) ? row["model_detail_list"] : [];
609
+ for (const detail of details) {
610
+ const w = traeMaxContextWindow(detail);
611
+ if (w !== undefined && (ctx === undefined || w > ctx)) ctx = w;
612
+ }
613
+ out.push({ id, ctx });
614
+ }
615
+ }
616
+ return out;
617
+ }
618
+
619
+ // CN: the chat roster surface.
620
+ const url = `${core}${TRAE_MODEL_LIST_PATH}`;
531
621
  const response = await input.fetcher(url, {
532
622
  method: "POST",
533
623
  headers,
@@ -550,8 +640,8 @@ export async function discoverTraeModels(input: {
550
640
  return rows
551
641
  .filter((row): row is Record<string, unknown> => typeof row === "object" && row !== null)
552
642
  .map((row) => ({
553
- // `model_name` is the key upstream's listing uses; the others are the
554
- // spellings the older donors assumed.
643
+ // `model_name` is the key upstream's listing uses; `name`/`id` are
644
+ // spellings older snapshots assumed.
555
645
  id: String(row["model_name"] ?? row["name"] ?? row["id"] ?? "").trim(),
556
646
  ctx: traeMaxContextWindow(row),
557
647
  }))