cartethyia 2.7.0 → 2.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (320) hide show
  1. package/.env.example +107 -106
  2. package/README.md +25 -2
  3. package/dist/dashboard/assets/{BannedPage-P76QLGJA.js → BannedPage-D-J75hBM.js} +1 -1
  4. package/dist/dashboard/assets/BannedPage-D-J75hBM.js.gz +0 -0
  5. package/dist/dashboard/assets/{CliToolDetailPage-DdBggJS9.js → CliToolDetailPage-CVe6RaBH.js} +1 -1
  6. package/dist/dashboard/assets/CliToolDetailPage-CVe6RaBH.js.gz +0 -0
  7. package/dist/dashboard/assets/{CliToolsPage-aN1iXhhP.js → CliToolsPage-Dd8ZSamp.js} +1 -1
  8. package/dist/dashboard/assets/CliToolsPage-Dd8ZSamp.js.gz +0 -0
  9. package/dist/dashboard/assets/{CombosPage-DE-F1nxZ.js → CombosPage-QoJ6foxo.js} +1 -1
  10. package/dist/dashboard/assets/CombosPage-QoJ6foxo.js.gz +0 -0
  11. package/dist/dashboard/assets/ConsoleLogPage-DpNRay4C.js +1 -0
  12. package/dist/dashboard/assets/ConsoleLogPage-DpNRay4C.js.gz +0 -0
  13. package/dist/dashboard/assets/{CustomizationPage-3CUxMy2X.js → CustomizationPage-CKUFTcW4.js} +1 -1
  14. package/dist/dashboard/assets/CustomizationPage-CKUFTcW4.js.gz +0 -0
  15. package/dist/dashboard/assets/HealthEventsModal-CzSLiDzr.js +1 -0
  16. package/dist/dashboard/assets/HealthEventsModal-CzSLiDzr.js.gz +0 -0
  17. package/dist/dashboard/assets/LoginPage-DubdQcvX.js +1 -0
  18. package/dist/dashboard/assets/LoginPage-DubdQcvX.js.gz +0 -0
  19. package/dist/dashboard/assets/{ModelPicker-DKVslqG3.js → ModelPicker-BH8Tehdi.js} +1 -1
  20. package/dist/dashboard/assets/ModelPicker-BH8Tehdi.js.gz +0 -0
  21. package/dist/dashboard/assets/OverviewPage-DuWzAQFL.js +1 -0
  22. package/dist/dashboard/assets/OverviewPage-DuWzAQFL.js.gz +0 -0
  23. package/dist/dashboard/assets/ProviderDetailPage-BKEKe2Fm.js +4 -0
  24. package/dist/dashboard/assets/ProviderDetailPage-BKEKe2Fm.js.gz +0 -0
  25. package/dist/dashboard/assets/ProviderIcon-FB6yS4v1.js +1 -0
  26. package/dist/dashboard/assets/ProviderIcon-FB6yS4v1.js.gz +0 -0
  27. package/dist/dashboard/assets/ProvidersPage-Bwotu8UJ.js +1 -0
  28. package/dist/dashboard/assets/ProvidersPage-Bwotu8UJ.js.gz +0 -0
  29. package/dist/dashboard/assets/ProxyPage-D7hpWbg9.js +9 -0
  30. package/dist/dashboard/assets/ProxyPage-D7hpWbg9.js.gz +0 -0
  31. package/dist/dashboard/assets/{QuotaPage-BjldQwIU.js → QuotaPage-ATpaxrbl.js} +1 -1
  32. package/dist/dashboard/assets/QuotaPage-ATpaxrbl.js.gz +0 -0
  33. package/dist/dashboard/assets/{SettingsPage-DUIoLRxk.js → SettingsPage-CETophpx.js} +1 -1
  34. package/dist/dashboard/assets/SettingsPage-CETophpx.js.gz +0 -0
  35. package/dist/dashboard/assets/SetupPage-BdgECjqq.js +1 -0
  36. package/dist/dashboard/assets/SetupPage-BdgECjqq.js.gz +0 -0
  37. package/dist/dashboard/assets/{SortableList-fI7JkdIB.js → SortableList-CAK-EN9z.js} +1 -1
  38. package/dist/dashboard/assets/SortableList-CAK-EN9z.js.gz +0 -0
  39. package/dist/dashboard/assets/{StudioPage-CHVZqskZ.js → StudioPage-B7W2_RhT.js} +3 -3
  40. package/dist/dashboard/assets/StudioPage-B7W2_RhT.js.gz +0 -0
  41. package/dist/dashboard/assets/UsagePage-6T4EFDjW.js +56 -0
  42. package/dist/dashboard/assets/UsagePage-6T4EFDjW.js.gz +0 -0
  43. package/dist/dashboard/assets/{api-keys-DFY_fZU4.js → api-keys-CUi6UXsR.js} +1 -1
  44. package/dist/dashboard/assets/api-keys-CUi6UXsR.js.gz +0 -0
  45. package/dist/dashboard/assets/app-KvsUJUMF.css +2 -0
  46. package/dist/dashboard/assets/app-KvsUJUMF.css.gz +0 -0
  47. package/dist/dashboard/assets/{assert-CwdnZARt.js → assert-67xXH5rf.js} +1 -1
  48. package/dist/dashboard/assets/assert-67xXH5rf.js.gz +0 -0
  49. package/dist/dashboard/assets/{badge-Dr-uf0Au.js → badge-DC2L43Mj.js} +1 -1
  50. package/dist/dashboard/assets/badge-DC2L43Mj.js.gz +0 -0
  51. package/dist/dashboard/assets/{banner-BddmMdoS.js → banner-BBVcb0J3.js} +1 -1
  52. package/dist/dashboard/assets/banner-BBVcb0J3.js.gz +0 -0
  53. package/dist/dashboard/assets/{card-CK0PXWlX.js → card-Cca1XC6G.js} +1 -1
  54. package/dist/dashboard/assets/card-Cca1XC6G.js.gz +0 -0
  55. package/dist/dashboard/assets/{cli-tools-CcB6mGh2.js → cli-tools-KvsWa1Jl.js} +1 -1
  56. package/dist/dashboard/assets/cli-tools-KvsWa1Jl.js.gz +0 -0
  57. package/dist/dashboard/assets/{clipboard-button-DGb2P9Yx.js → clipboard-button-DUzEbynq.js} +1 -1
  58. package/dist/dashboard/assets/clipboard-button-DUzEbynq.js.gz +0 -0
  59. package/dist/dashboard/assets/{dialog-LkfuL50u.js → dialog-DFvWhwTa.js} +1 -1
  60. package/dist/dashboard/assets/dialog-DFvWhwTa.js.gz +0 -0
  61. package/dist/dashboard/assets/{drawer-ZbEZ_84c.js → drawer-CFM2sMX1.js} +1 -1
  62. package/dist/dashboard/assets/drawer-CFM2sMX1.js.gz +0 -0
  63. package/dist/dashboard/assets/{entry-CWBY5A0f.js → entry-BLBLxX0h.js} +3 -3
  64. package/dist/dashboard/assets/entry-BLBLxX0h.js.gz +0 -0
  65. package/dist/dashboard/assets/{entry-BJfsVMvA.js → entry-Bqy8RFvM.js} +1 -1
  66. package/dist/dashboard/assets/entry-Bqy8RFvM.js.gz +0 -0
  67. package/dist/dashboard/assets/{entry-CvC1-Zqx.js → entry-_xHo5x6r.js} +1 -1
  68. package/dist/dashboard/assets/entry-_xHo5x6r.js.gz +0 -0
  69. package/dist/dashboard/assets/formatters-mpBPqAQ8.js +1 -0
  70. package/dist/dashboard/assets/formatters-mpBPqAQ8.js.gz +0 -0
  71. package/dist/dashboard/assets/{github-badge-BxpznFLt.js → github-badge-C9e8Ts7e.js} +1 -1
  72. package/dist/dashboard/assets/github-badge-C9e8Ts7e.js.gz +0 -0
  73. package/dist/dashboard/assets/{index-CDzP3ovJ.js → index-D6QzEC_n.js} +2 -2
  74. package/dist/dashboard/assets/index-D6QzEC_n.js.gz +0 -0
  75. package/dist/dashboard/assets/{inline-Ae13MQ5w.js → inline-BFUcvllj.js} +1 -1
  76. package/dist/dashboard/assets/inline-BFUcvllj.js.gz +0 -0
  77. package/dist/dashboard/assets/{input-D23fo7cp.js → input-C_ILAvBb.js} +1 -1
  78. package/dist/dashboard/assets/input-C_ILAvBb.js.gz +0 -0
  79. package/dist/dashboard/assets/{layout-CSsxjj5P.js → layout-CBIyT6Rt.js} +1 -1
  80. package/dist/dashboard/assets/layout-CBIyT6Rt.js.gz +0 -0
  81. package/dist/dashboard/assets/{list-row-dmbwZMDY.js → list-row-ebJ_-Nsb.js} +1 -1
  82. package/dist/dashboard/assets/list-row-ebJ_-Nsb.js.gz +0 -0
  83. package/dist/dashboard/assets/{live-BG1oUsZZ.js → live-D0mm--ME.js} +1 -1
  84. package/dist/dashboard/assets/live-D0mm--ME.js.gz +0 -0
  85. package/dist/dashboard/assets/{mutation-DSF-e4ho.js → mutation-sMD965oO.js} +1 -1
  86. package/dist/dashboard/assets/mutation-sMD965oO.js.gz +0 -0
  87. package/dist/dashboard/assets/{numbers-Bm_aECY7.js → numbers-BO9dKaFr.js} +1 -1
  88. package/dist/dashboard/assets/numbers-BO9dKaFr.js.gz +0 -0
  89. package/dist/dashboard/assets/providers/inception.svg +5 -0
  90. package/dist/dashboard/assets/providers/inception.svg.gz +0 -0
  91. package/dist/dashboard/assets/{query-keys-CF56nYIV.js → query-keys-CfL_Zdoh.js} +1 -1
  92. package/dist/dashboard/assets/query-keys-CfL_Zdoh.js.gz +0 -0
  93. package/dist/dashboard/assets/{routing-CeA71n7G.js → routing-BnIpi1IJ.js} +1 -1
  94. package/dist/dashboard/assets/routing-BnIpi1IJ.js.gz +0 -0
  95. package/dist/dashboard/assets/{select-KIT0rC6G.js → select-Cj5Nw43g.js} +1 -1
  96. package/dist/dashboard/assets/select-Cj5Nw43g.js.gz +0 -0
  97. package/dist/dashboard/assets/{settings-B_2gkePf.js → settings-BAFhDgX8.js} +1 -1
  98. package/dist/dashboard/assets/settings-BAFhDgX8.js.gz +0 -0
  99. package/dist/dashboard/assets/{stack-D7WOSzcX.js → stack-EMrOMt4I.js} +1 -1
  100. package/dist/dashboard/assets/stack-EMrOMt4I.js.gz +0 -0
  101. package/dist/dashboard/assets/{state-BBxtKZOQ.js → state-CWoo7ty5.js} +1 -1
  102. package/dist/dashboard/assets/state-CWoo7ty5.js.gz +0 -0
  103. package/dist/dashboard/assets/{switch-DNS_D5Ea.js → switch-CCjT6IkF.js} +1 -1
  104. package/dist/dashboard/assets/switch-CCjT6IkF.js.gz +0 -0
  105. package/dist/dashboard/assets/{useMutation-BgoxdoMk.js → useMutation-DjkLHUcE.js} +1 -1
  106. package/dist/dashboard/assets/useMutation-DjkLHUcE.js.gz +0 -0
  107. package/dist/dashboard/index.html +1 -1
  108. package/dist/dashboard/index.html.gz +0 -0
  109. package/dist/dashboard/precompressed-assets.json +54 -53
  110. package/migrations/full-postgres/0024_add_pool_provider_cooldowns.sql +12 -0
  111. package/migrations/full-postgres/0025_add_pool_random_strategy.sql +3 -0
  112. package/migrations/lite-sqlite3/0024_add_pool_provider_cooldowns.sql +12 -0
  113. package/migrations/lite-sqlite3/0025_add_pool_random_strategy.sql +20 -0
  114. package/package.json +96 -96
  115. package/scripts/cli/console-menu.ts +5 -2
  116. package/scripts/cli/tray.ts +21 -6
  117. package/src/boot/app.ts +684 -714
  118. package/src/boot/dependencies.ts +65 -62
  119. package/src/config/cooldowns.ts +16 -0
  120. package/src/config/providers.ts +13 -1
  121. package/src/config/resources.ts +70 -70
  122. package/src/config/security.ts +37 -37
  123. package/src/config/spec.ts +371 -358
  124. package/src/console/console-router.ts +233 -225
  125. package/src/console/domain-registration.ts +398 -381
  126. package/src/console/providers/catalog/catalog-projections.ts +2 -6
  127. package/src/console/providers/catalog/contracts.ts +19 -0
  128. package/src/console/providers/catalog/provider-operations.ts +38 -0
  129. package/src/console/providers/catalog/routes.ts +14 -0
  130. package/src/console/providers/catalog/store-helpers.ts +326 -0
  131. package/src/console/providers/catalog/store.ts +1292 -1563
  132. package/src/console/quota/account-quota-global.ts +2 -2
  133. package/src/console/quota/account-quota-shared.ts +1 -1
  134. package/src/console/quota/account-quota-tenant.ts +35 -12
  135. package/src/console/quota/quota-view.ts +2 -2
  136. package/src/console/routing/pools/contracts.ts +41 -1
  137. package/src/console/routing/pools/operations.ts +183 -4
  138. package/src/console/routing/pools/relay-deploy.ts +17 -4
  139. package/src/console/routing/pools/routes.ts +30 -0
  140. package/src/console/routing/pools/store.ts +98 -8
  141. package/src/main.ts +183 -167
  142. package/src/network/outbound-fetch.ts +226 -365
  143. package/src/network/pool/agent.ts +8 -0
  144. package/src/network/pool/pool-state.ts +7 -0
  145. package/src/network/pool/selector.ts +110 -2
  146. package/src/network/pool-cooldown-store.ts +252 -0
  147. package/src/network/pool-health-machine.ts +5 -3
  148. package/src/network/pool-health.ts +36 -9
  149. package/src/observability/event-query.ts +57 -0
  150. package/src/observability/metrics.ts +467 -467
  151. package/src/observability/runtime-metrics.ts +269 -269
  152. package/src/persistence/health-event-flush.ts +2 -0
  153. package/src/persistence/schema.ts +19 -0
  154. package/src/persistence/usage-flush.ts +8 -3
  155. package/src/protocol/canonical-model.ts +1 -1
  156. package/src/protocol/dsml.ts +377 -0
  157. package/src/protocol/gateway-error.ts +232 -232
  158. package/src/{providers/provider-reasoning.ts → protocol/reasoning.ts} +10 -9
  159. package/src/protocol/request/chat.ts +7 -7
  160. package/src/protocol/request/codex.ts +2 -2
  161. package/src/protocol/request/gemini.ts +1 -1
  162. package/src/protocol/request/messages.ts +6 -6
  163. package/src/protocol/request/responses.ts +7 -7
  164. package/src/protocol/response/dsml.ts +12 -133
  165. package/src/{transport → protocol}/translation/attachment-integrity.ts +2 -2
  166. package/src/{transport → protocol}/translation/cache-controls.ts +1 -1
  167. package/src/{transport → protocol}/translation/capabilities.ts +2 -2
  168. package/src/{transport → protocol}/translation/normalize.ts +1 -1
  169. package/src/{transport → protocol}/translation/quirks.ts +1 -1
  170. package/src/{transport → protocol}/translation/search.ts +1 -1
  171. package/src/{transport → protocol}/translation/thinking.ts +23 -1
  172. package/src/{transport → protocol}/translation/tool-id.ts +1 -1
  173. package/src/{transport → protocol}/translation/tool-repair.ts +2 -2
  174. package/src/protocol/upstream-parsing.ts +176 -0
  175. package/src/providers/adapters/compatible-adapter.ts +11 -2
  176. package/src/providers/authentication/oauth-refresh-service.ts +9 -5
  177. package/src/providers/default-registry.ts +21 -5
  178. package/src/providers/discovery/probe-phases.ts +16 -3
  179. package/src/providers/discovery/probing-service.ts +4 -1
  180. package/src/providers/integrations/anthropic.ts +1 -1
  181. package/src/providers/integrations/buddy/buddy-catalog-shared.ts +1 -4
  182. package/src/providers/integrations/buddy/buddy-chat-shared.ts +141 -8
  183. package/src/providers/integrations/buddy/buddy-checkin.ts +5 -1
  184. package/src/providers/integrations/buddy/buddy-growth-shared.ts +0 -82
  185. package/src/providers/integrations/buddy/buddy-identity.ts +25 -0
  186. package/src/providers/integrations/buddy/buddy-server-tools.ts +240 -0
  187. package/src/providers/integrations/buddy/codebuddy/cn-catalog.ts +36 -0
  188. package/src/providers/integrations/buddy/codebuddy/cn-provider.ts +6 -31
  189. package/src/providers/integrations/buddy/codebuddy/provider.ts +2 -0
  190. package/src/providers/integrations/buddy/codebuddy/shared.ts +19 -4
  191. package/src/providers/integrations/buddy/turing-device-token.ts +262 -0
  192. package/src/providers/integrations/buddy/workbuddy/provider.ts +2 -0
  193. package/src/providers/integrations/buddy/workbuddy/shared.ts +56 -3
  194. package/src/providers/integrations/claude/claude.ts +1 -1
  195. package/src/providers/integrations/cloudflareai/cloudflareai.ts +1 -1
  196. package/src/providers/integrations/codex/codex-errors.ts +1 -1
  197. package/src/providers/integrations/codex/codex.ts +1 -1
  198. package/src/providers/integrations/factory/factory.ts +1 -1
  199. package/src/providers/integrations/github/github.ts +1 -1
  200. package/src/providers/integrations/grok/grok.ts +1 -1
  201. package/src/providers/integrations/inception/provider.ts +91 -0
  202. package/src/providers/integrations/inferhub.ts +1 -1
  203. package/src/providers/integrations/kiro/kiro-request.ts +1 -1
  204. package/src/providers/integrations/mistral.ts +193 -19
  205. package/src/providers/integrations/opencode/fingerprint.ts +19 -2
  206. package/src/providers/integrations/opencode/prompt.ts +111 -0
  207. package/src/providers/integrations/opencode/provider.ts +101 -23
  208. package/src/providers/integrations/opencode/tool-definitions.ts +4 -4
  209. package/src/providers/integrations/qoder/qoder-checkin.ts +303 -0
  210. package/src/providers/integrations/qoder/qoder.ts +1641 -1458
  211. package/src/providers/integrations/shared/install-id.ts +2 -1
  212. package/src/providers/integrations/trae/trae-cache.ts +110 -0
  213. package/src/providers/integrations/trae/trae-oauth.ts +29 -4
  214. package/src/providers/integrations/trae/trae-protocol.ts +2 -1
  215. package/src/providers/integrations/trae/trae.ts +106 -23
  216. package/src/providers/operations/account-error-classification.ts +1660 -0
  217. package/src/providers/operations/account-health-service.ts +621 -2097
  218. package/src/providers/operations/account-reset-service.ts +2 -0
  219. package/src/providers/provider-contracts.ts +0 -2
  220. package/src/providers/provider-metadata.ts +25 -4
  221. package/src/providers/provider-probe.ts +2 -2
  222. package/src/{console → providers}/quota/cache.ts +1 -1
  223. package/src/{console → providers}/quota/refresh.ts +5 -5
  224. package/src/transport/dispatch/attempt-finalize.ts +114 -12
  225. package/src/transport/dispatch/attempt-loop.ts +3 -0
  226. package/src/transport/dispatch/empty-stream.ts +224 -217
  227. package/src/transport/dispatch/generation.ts +37 -55
  228. package/src/transport/dispatch/handler-deps.ts +32 -0
  229. package/src/transport/dispatch/leases.ts +9 -4
  230. package/src/transport/dispatch/proxy-request.ts +6 -30
  231. package/src/transport/dispatch/responses-compact.ts +22 -43
  232. package/src/transport/dispatch/streaming/finalize.ts +3 -0
  233. package/src/transport/dispatch/streaming/stream.ts +0 -8
  234. package/src/transport/dispatch/systemone.ts +22 -42
  235. package/src/transport/dispatch/token-count.ts +23 -39
  236. package/src/transport/dispatch/upstream.ts +1 -3
  237. package/src/transport/dispatch/websearch.ts +22 -41
  238. package/src/transport/failure-policy.ts +59 -178
  239. package/src/transport/guards/abuse-guard.ts +10 -0
  240. package/src/transport/guards/auth-guard.ts +109 -109
  241. package/src/transport/guards/error-lifecycle.ts +1 -1
  242. package/src/transport/guards/pipeline.ts +220 -220
  243. package/src/transport/guards/request-context.ts +245 -238
  244. package/src/transport/request/degradation.ts +20 -1
  245. package/src/transport/request/inflight.ts +4 -114
  246. package/src/transport/request/preparer.ts +6 -6
  247. package/src/transport/request/state.ts +66 -38
  248. package/src/transport/request/tenant-preferences.ts +1 -1
  249. package/src/transport/routing/route-catalog.ts +41 -31
  250. package/src/transport/routing/router.ts +2 -2
  251. package/src/transport/surface/messages/parse.ts +1 -1
  252. package/src/workers/buddy-growth-tasks.ts +346 -425
  253. package/src/workers/daily-checkin.ts +17 -6
  254. package/src/workers/quota-refresh-sweep.ts +2 -2
  255. package/src/workers/scheduler.ts +17 -1
  256. package/dist/dashboard/assets/BannedPage-P76QLGJA.js.gz +0 -0
  257. package/dist/dashboard/assets/CliToolDetailPage-DdBggJS9.js.gz +0 -0
  258. package/dist/dashboard/assets/CliToolsPage-aN1iXhhP.js.gz +0 -0
  259. package/dist/dashboard/assets/CombosPage-DE-F1nxZ.js.gz +0 -0
  260. package/dist/dashboard/assets/ConsoleLogPage-5SpmfLyw.js +0 -1
  261. package/dist/dashboard/assets/ConsoleLogPage-5SpmfLyw.js.gz +0 -0
  262. package/dist/dashboard/assets/CustomizationPage-3CUxMy2X.js.gz +0 -0
  263. package/dist/dashboard/assets/HealthEventsModal-BkmJQKC0.js +0 -1
  264. package/dist/dashboard/assets/HealthEventsModal-BkmJQKC0.js.gz +0 -0
  265. package/dist/dashboard/assets/LoginPage-B_0FUKWc.js +0 -1
  266. package/dist/dashboard/assets/LoginPage-B_0FUKWc.js.gz +0 -0
  267. package/dist/dashboard/assets/ModelPicker-DKVslqG3.js.gz +0 -0
  268. package/dist/dashboard/assets/OverviewPage-D-H4MEiT.js +0 -1
  269. package/dist/dashboard/assets/OverviewPage-D-H4MEiT.js.gz +0 -0
  270. package/dist/dashboard/assets/ProviderDetailPage-CEJw6Guo.js +0 -3
  271. package/dist/dashboard/assets/ProviderDetailPage-CEJw6Guo.js.gz +0 -0
  272. package/dist/dashboard/assets/ProviderIcon-CmfzxpVM.js +0 -1
  273. package/dist/dashboard/assets/ProviderIcon-CmfzxpVM.js.gz +0 -0
  274. package/dist/dashboard/assets/ProvidersPage-CEplAR-i.js +0 -1
  275. package/dist/dashboard/assets/ProvidersPage-CEplAR-i.js.gz +0 -0
  276. package/dist/dashboard/assets/ProxyPage-CAN7hy0f.js +0 -7
  277. package/dist/dashboard/assets/ProxyPage-CAN7hy0f.js.gz +0 -0
  278. package/dist/dashboard/assets/QuotaPage-BjldQwIU.js.gz +0 -0
  279. package/dist/dashboard/assets/SettingsPage-DUIoLRxk.js.gz +0 -0
  280. package/dist/dashboard/assets/SetupPage-_bCaTM0v.js +0 -1
  281. package/dist/dashboard/assets/SetupPage-_bCaTM0v.js.gz +0 -0
  282. package/dist/dashboard/assets/SortableList-fI7JkdIB.js.gz +0 -0
  283. package/dist/dashboard/assets/StudioPage-CHVZqskZ.js.gz +0 -0
  284. package/dist/dashboard/assets/UsagePage-DiFBymHj.js +0 -56
  285. package/dist/dashboard/assets/UsagePage-DiFBymHj.js.gz +0 -0
  286. package/dist/dashboard/assets/api-keys-DFY_fZU4.js.gz +0 -0
  287. package/dist/dashboard/assets/app-BOJ1_xUB.css +0 -2
  288. package/dist/dashboard/assets/app-BOJ1_xUB.css.gz +0 -0
  289. package/dist/dashboard/assets/assert-CwdnZARt.js.gz +0 -0
  290. package/dist/dashboard/assets/badge-Dr-uf0Au.js.gz +0 -0
  291. package/dist/dashboard/assets/banner-BddmMdoS.js.gz +0 -0
  292. package/dist/dashboard/assets/card-CK0PXWlX.js.gz +0 -0
  293. package/dist/dashboard/assets/cli-tools-CcB6mGh2.js.gz +0 -0
  294. package/dist/dashboard/assets/clipboard-button-DGb2P9Yx.js.gz +0 -0
  295. package/dist/dashboard/assets/cog-CdWhsajs.js +0 -1
  296. package/dist/dashboard/assets/cog-CdWhsajs.js.gz +0 -0
  297. package/dist/dashboard/assets/dialog-LkfuL50u.js.gz +0 -0
  298. package/dist/dashboard/assets/drawer-ZbEZ_84c.js.gz +0 -0
  299. package/dist/dashboard/assets/entry-BJfsVMvA.js.gz +0 -0
  300. package/dist/dashboard/assets/entry-CWBY5A0f.js.gz +0 -0
  301. package/dist/dashboard/assets/entry-CvC1-Zqx.js.gz +0 -0
  302. package/dist/dashboard/assets/github-badge-BxpznFLt.js.gz +0 -0
  303. package/dist/dashboard/assets/index-CDzP3ovJ.js.gz +0 -0
  304. package/dist/dashboard/assets/inline-Ae13MQ5w.js.gz +0 -0
  305. package/dist/dashboard/assets/input-D23fo7cp.js.gz +0 -0
  306. package/dist/dashboard/assets/layout-CSsxjj5P.js.gz +0 -0
  307. package/dist/dashboard/assets/list-row-dmbwZMDY.js.gz +0 -0
  308. package/dist/dashboard/assets/live-BG1oUsZZ.js.gz +0 -0
  309. package/dist/dashboard/assets/mutation-DSF-e4ho.js.gz +0 -0
  310. package/dist/dashboard/assets/numbers-Bm_aECY7.js.gz +0 -0
  311. package/dist/dashboard/assets/query-keys-CF56nYIV.js.gz +0 -0
  312. package/dist/dashboard/assets/routing-CeA71n7G.js.gz +0 -0
  313. package/dist/dashboard/assets/select-KIT0rC6G.js.gz +0 -0
  314. package/dist/dashboard/assets/settings-B_2gkePf.js.gz +0 -0
  315. package/dist/dashboard/assets/stack-D7WOSzcX.js.gz +0 -0
  316. package/dist/dashboard/assets/state-BBxtKZOQ.js.gz +0 -0
  317. package/dist/dashboard/assets/switch-DNS_D5Ea.js.gz +0 -0
  318. package/dist/dashboard/assets/useMutation-BgoxdoMk.js.gz +0 -0
  319. /package/dist/dashboard/assets/{app-CaXZ9SCn.js → app-BaWd0puM.js} +0 -0
  320. /package/dist/dashboard/assets/{app-CaXZ9SCn.js.gz → app-BaWd0puM.js.gz} +0 -0
@@ -25,6 +25,7 @@ import { mkdir, open, readFile } from "node:fs/promises";
25
25
  import { dirname, join } from "node:path";
26
26
  import { randomUUID } from "node:crypto";
27
27
  import { log } from "../../../observability/logger";
28
+ import { resolveInstallIdDir } from "../../../config/providers";
28
29
 
29
30
  /** Overrides the directory the install ids are persisted in. */
30
31
  const INSTALL_ID_DIR_ENV = "CARTETHYIA_INSTALL_ID_DIR";
@@ -40,7 +41,7 @@ const INSTALL_ID_DIR_ENV = "CARTETHYIA_INSTALL_ID_DIR";
40
41
  */
41
42
  function installIdDirectories(): readonly string[] {
42
43
  const candidates: string[] = [];
43
- const configured = process.env[INSTALL_ID_DIR_ENV]?.trim();
44
+ const configured = resolveInstallIdDir()?.trim();
44
45
  if (configured && configured.length > 0) candidates.push(configured);
45
46
  const home = (process.env["HOME"] ?? process.env["USERPROFILE"])?.trim();
46
47
  if (home && home.length > 0) candidates.push(join(home, ".cartethyia"));
@@ -0,0 +1,110 @@
1
+ // Trae session-cache estimation.
2
+ //
3
+ // Trae's agent surface does not report prompt-cache hits: the upstream metadata
4
+ // carries `prompt_cache_pool_record: null` and every cache counter
5
+ // (`cache_read_input_tokens`, `cached_tokens`) stays 0 even for byte-identical
6
+ // prefixes. The underlying models do cache those prefixes, so for the same
7
+ // conversation we ESTIMATE the hit locally. This only fills the gap when
8
+ // upstream reports nothing (0 or unavailable); a genuine upstream report (>0)
9
+ // is trusted and left untouched.
10
+
11
+ import { createHash } from "node:crypto";
12
+ import type { CanonicalMessage, UsageRecord } from "../../../protocol/canonical-model";
13
+
14
+ /** Bounded in-memory tracker: one entry per session, oldest evicted first. */
15
+ const MAX_SESSIONS = 512;
16
+
17
+ interface TraeSessionCacheEntry {
18
+ /** hash(full messages) -> reported input tokens for that exact list. */
19
+ readonly seen: Map<string, number>;
20
+ /** Insertion order for bounded eviction. */
21
+ readonly order: string[];
22
+ }
23
+
24
+ const sessions = new Map<string, TraeSessionCacheEntry>();
25
+
26
+ function hashOf(messages: readonly CanonicalMessage[]): string {
27
+ return createHash("sha256").update(JSON.stringify(messages)).digest("hex");
28
+ }
29
+
30
+ function sessionFor(sessionId: string): TraeSessionCacheEntry {
31
+ let entry = sessions.get(sessionId);
32
+ if (!entry) {
33
+ entry = { seen: new Map(), order: [] };
34
+ sessions.set(sessionId, entry);
35
+ }
36
+ return entry;
37
+ }
38
+
39
+ function remember(sessionId: string, fullHash: string, inputTokens: number): void {
40
+ const entry = sessionFor(sessionId);
41
+ if (!entry.seen.has(fullHash)) {
42
+ entry.order.push(fullHash);
43
+ if (entry.order.length > MAX_SESSIONS) {
44
+ const oldest = entry.order.shift();
45
+ if (oldest !== undefined) entry.seen.delete(oldest);
46
+ }
47
+ }
48
+ entry.seen.set(fullHash, inputTokens);
49
+ if (sessions.size > MAX_SESSIONS) {
50
+ const oldestSession = sessions.keys().next().value;
51
+ if (oldestSession !== undefined) sessions.delete(oldestSession);
52
+ }
53
+ }
54
+
55
+ /**
56
+ * Estimates how much of this request's input was served from cache.
57
+ *
58
+ * The longest prefix of `messages` that was ever sent as a complete request in
59
+ * this session is considered cached, and its recorded token count is the
60
+ * cached portion — if that prefix is the whole request, everything was cached.
61
+ * This models how upstream prompt caching actually behaves (byte-identical
62
+ * prefix reuse) without trusting a counter the upstream never reports.
63
+ *
64
+ * Returns the record unchanged when there is nothing to estimate (no session,
65
+ * no history, or upstream already reported a real hit).
66
+ */
67
+ export function estimateTraeSessionCache(
68
+ record: UsageRecord,
69
+ sessionId: string | undefined,
70
+ messages: readonly CanonicalMessage[] | undefined,
71
+ ): UsageRecord {
72
+ if (!sessionId || !messages || messages.length === 0) return record;
73
+ const upstreamReported =
74
+ typeof record.cached_input_tokens === "number" && record.cached_input_tokens > 0;
75
+ if (upstreamReported) return record;
76
+
77
+ const fullHash = hashOf(messages);
78
+ const entry = sessions.get(sessionId);
79
+
80
+ if (entry) {
81
+ // Longest prefix that matches a previously sent complete request.
82
+ for (let len = messages.length; len >= 1; len -= 1) {
83
+ const prefix = messages.slice(0, len);
84
+ const tokens = entry.seen.get(hashOf(prefix));
85
+ if (tokens === undefined) continue;
86
+ if (len === messages.length) {
87
+ // Byte-identical request: the whole input came from cache.
88
+ return {
89
+ ...record,
90
+ cached_input_tokens: record.input_tokens,
91
+ uncached_input_tokens: 0,
92
+ };
93
+ }
94
+ const cached = Math.min(tokens, record.input_tokens);
95
+ return {
96
+ ...record,
97
+ cached_input_tokens: cached,
98
+ uncached_input_tokens: Math.max(0, record.input_tokens - cached),
99
+ };
100
+ }
101
+ }
102
+
103
+ remember(sessionId, fullHash, record.input_tokens);
104
+ return record;
105
+ }
106
+
107
+ /** Drops all tracked sessions. Test-only. */
108
+ export function resetTraeSessionCacheForTesting(): void {
109
+ sessions.clear();
110
+ }
@@ -505,9 +505,15 @@ export class TraeOAuthClient extends OAuthClient {
505
505
  override async refresh(
506
506
  refreshToken: string,
507
507
  signal?: AbortSignal,
508
- context?: { readonly account_id?: string },
508
+ context?: { readonly account_id?: string; readonly auth_state?: Readonly<Record<string, unknown>> },
509
509
  ): Promise<OAuthTokenRefreshResult> {
510
- return this.refreshWithUser(refreshToken, context?.account_id ?? "", signal);
510
+ // The exchange is addressed per Trae account, not per gateway account row:
511
+ // `UserID` must be the upstream user id. The login flow stores it in
512
+ // `auth_state.userId`, and re-imports carry it the same way — preferring it
513
+ // over `account_id` keeps refresh working for accounts whose row id is not
514
+ // the upstream user id (any account created before the id was persisted).
515
+ const upstreamUserId = firstString(context?.auth_state ?? {}, ["userId", "user_id", "uid"]);
516
+ return this.refreshWithUser(refreshToken, upstreamUserId ?? context?.account_id ?? "", signal);
511
517
  }
512
518
 
513
519
  async #exchange(
@@ -577,12 +583,15 @@ function parsedResult(
577
583
  refresh: string,
578
584
  isStatic: boolean,
579
585
  ): OAuthExchangeResult {
586
+ const state: Record<string, unknown> = {};
587
+ if (isStatic) state["staticToken"] = true;
588
+ if (parsed.userId !== undefined) state["userId"] = parsed.userId;
580
589
  return {
581
590
  access,
582
591
  refresh,
583
592
  expiresAt:
584
593
  parsed.expiresAtMs === undefined ? new Date(Date.now() + 3600_000) : new Date(parsed.expiresAtMs),
585
- ...(isStatic ? { auth_state: { staticToken: true } } : {}),
594
+ ...(Object.keys(state).length === 0 ? {} : { auth_state: state }),
586
595
  ...(parsed.email === undefined ? {} : { accountLabel: parsed.email }),
587
596
  };
588
597
  }
@@ -619,7 +628,12 @@ function parseExport(credential: string): TraeExport | undefined {
619
628
  const refresh = firstString(parsed, ["refreshToken", "refresh_token", "refresh"]);
620
629
  const expiresAtMs = firstNumber(parsed, ["expiredAt", "expiresAt", "TokenExpireAt", "tokenExpireAt"]);
621
630
  const email = firstString(parsed, ["email", "userEmail", "accountLabel"]);
622
- const userId = firstString(parsed, ["userId", "user_id", "uid", "UserID"]);
631
+ // The exporter nests the user id under `authState`; a hand-built export may
632
+ // carry it at the top level instead.
633
+ const authState = firstRecord(parsed, ["authState", "auth_state"]);
634
+ const userId =
635
+ firstString(authState ?? {}, ["userId", "user_id", "uid", "UserID"]) ??
636
+ firstString(parsed, ["userId", "user_id", "uid", "UserID"]);
623
637
  return {
624
638
  access,
625
639
  ...(refresh === undefined ? {} : { refresh }),
@@ -629,6 +643,17 @@ function parseExport(credential: string): TraeExport | undefined {
629
643
  };
630
644
  }
631
645
 
646
+ /** First object value present under any of `keys`, or undefined. */
647
+ function firstRecord(record: Record<string, unknown>, keys: readonly string[]): Record<string, unknown> | undefined {
648
+ for (const key of keys) {
649
+ const value = record[key];
650
+ if (typeof value === "object" && value !== null && !Array.isArray(value)) {
651
+ return value as Record<string, unknown>;
652
+ }
653
+ }
654
+ return undefined;
655
+ }
656
+
632
657
  function firstNumber(record: Record<string, unknown>, keys: readonly string[]): number | undefined {
633
658
  for (const key of keys) {
634
659
  const value = record[key];
@@ -160,6 +160,7 @@ export function buildTraeChatBody(
160
160
  modelId: string,
161
161
  request: CanonicalRequest,
162
162
  realm: TraeRealm,
163
+ sessionId?: string,
163
164
  ): TraeChatBody {
164
165
  if (modelId.length === 0) throw new Error("trae model name is required");
165
166
 
@@ -235,7 +236,7 @@ export function buildTraeChatBody(
235
236
  stream: request.stream,
236
237
  region: config.region,
237
238
  ai_region: config.region,
238
- ...(request.session_id ? { session_id: request.session_id } : {}),
239
+ ...(request.session_id ?? sessionId ? { session_id: request.session_id ?? sessionId } : {}),
239
240
  };
240
241
  /**
241
242
  * Tool declarations, with `parameters` serialized to a string.
@@ -5,7 +5,8 @@
5
5
  // `Authorization: Cloud-IDE-JWT <token>` per the Trae 1.107.1 bundle.
6
6
 
7
7
  import { GatewayError } from "../../../protocol/gateway-error";
8
- import type { CanonicalEvent, CanonicalRequest, CanonicalStopReason } from "../../../protocol/canonical-model";
8
+ import type { CanonicalEvent, CanonicalMessage, CanonicalRequest, CanonicalStopReason } from "../../../protocol/canonical-model";
9
+ import type { UsageRecord } from "../../../protocol/canonical-model";
9
10
  import { usageFromProvider } from "../../provider-usage";
10
11
  import {
11
12
  readCredentialSecret,
@@ -16,6 +17,8 @@ import {
16
17
  } from "../../provider-contracts";
17
18
  import { mapUpstreamHttpError } from "../../../transport/failure-policy";
18
19
  import { abortGatewayError, createUpstreamDeadlineLifecycle } from "../../operations/upstream-deadline";
20
+ import { resolvePromptCacheKey } from "../../operations/session-resolution";
21
+ import { estimateTraeSessionCache } from "./trae-cache";
19
22
  import { TRAE_CN_MODELS, TRAE_INTL_MODELS, traeMaxContextWindow } from "./trae-catalog";
20
23
  import { buildTraeHeaders } from "./trae-headers";
21
24
  import {
@@ -86,6 +89,53 @@ function extractToken(secret: string): string | undefined {
86
89
  return trimmed.length > 0 ? trimmed : undefined;
87
90
  }
88
91
 
92
+ /**
93
+ * Maps a Trae upstream error (a named `error` SSE event, or an error object
94
+ * in a frame body) to the canonical GatewayError.
95
+ *
96
+ * The mapping mirrors the reference bridge's `TRAE_ERROR_MAP`:
97
+ * - `1001` — "we are not able to authenticate you" — is a credential verdict.
98
+ * It must surface as account-scoped `authentication_failed` so the account
99
+ * is parked for re-login and failover tries a sibling — never the proxy
100
+ * pool.
101
+ * - `4008` / "exceeded the quota" — free-tier quota exhausted. That is a
102
+ * `quota_exceeded` 429, not a platform fault.
103
+ * - Everything else is a platform fault (`platform_unavailable`).
104
+ */
105
+ function throwTraeUpstreamError(codeRaw: unknown, messageRaw: unknown): never {
106
+ const code =
107
+ typeof codeRaw === "number" || typeof codeRaw === "string" ? String(codeRaw) : undefined;
108
+ const message =
109
+ typeof messageRaw === "string" && messageRaw.trim().length > 0
110
+ ? messageRaw.trim()
111
+ : code !== undefined
112
+ ? `Trae stream error (upstream code ${code})`
113
+ : "Trae stream error";
114
+
115
+ if (code === "1001") {
116
+ throw new GatewayError(
117
+ "authentication_failed",
118
+ 401,
119
+ `Trae could not authenticate the account (upstream code 1001): ${message}`,
120
+ { providerCode: "1001", providerStatus: 1001, credentialEvidence: true },
121
+ "upstream",
122
+ );
123
+ }
124
+
125
+ const lower = message.toLowerCase();
126
+ if (code === "4008" || lower.includes("exceeded the quota") || lower.includes("quota")) {
127
+ throw new GatewayError(
128
+ "quota_exceeded",
129
+ 429,
130
+ `Trae quota exceeded (upstream code ${code ?? "unknown"}): ${message}`,
131
+ { providerCode: code ?? "4008", providerStatus: code === "4008" ? 4008 : 429 },
132
+ "upstream",
133
+ );
134
+ }
135
+
136
+ throw new GatewayError("platform_unavailable", 502, message, {}, "upstream");
137
+ }
138
+
89
139
  class TraeAdapter implements ProviderAdapter {
90
140
  readonly provider_id: ProviderId;
91
141
  private readonly realm: TraeRealm;
@@ -115,7 +165,21 @@ class TraeAdapter implements ProviderAdapter {
115
165
  (context.outbound_fetch as unknown as typeof fetch | undefined) ?? this.fetchFn;
116
166
 
117
167
  const auth = traeAuth(secret, context.credential.account_id);
118
- const body = buildTraeChatBody(modelId, request, this.realm);
168
+ /**
169
+ * Trae keeps upstream prompt-cache state keyed by the request's
170
+ * `session_id`. The canonical request rarely carries one (surfaces do not
171
+ * populate it), so without this the body always omitted `session_id` and
172
+ * upstream treated every turn as a fresh context — `cached_tokens: 0`
173
+ * even for byte-identical prefixes. Resolve the same session/cache key the
174
+ * other adapters use (caller key → inbound session headers → opening-turn
175
+ * hash) and thread it through.
176
+ */
177
+ const sessionId = request.session_id ?? resolvePromptCacheKey(request, context);
178
+ const body = buildTraeChatBody(modelId, request, this.realm, sessionId);
179
+ // The cache estimator is scoped per (session, model): upstream cache keys
180
+ // are model-specific, so two models sharing a conversation id must not
181
+ // treat each other's identical prefixes as hits.
182
+ const cacheSessionKey = sessionId === undefined ? undefined : `${modelId}:${sessionId}`;
119
183
  /**
120
184
  * The endpoint follows the model, not the provider.
121
185
  *
@@ -159,6 +223,8 @@ class TraeAdapter implements ProviderAdapter {
159
223
  response.body as ReadableStream<Uint8Array>,
160
224
  lifecycle.signal,
161
225
  request.model,
226
+ cacheSessionKey,
227
+ request.messages,
162
228
  );
163
229
  } catch (err: unknown) {
164
230
  const abortError = abortGatewayError(lifecycle, err, context.abort_signal);
@@ -173,6 +239,8 @@ class TraeAdapter implements ProviderAdapter {
173
239
  body: ReadableStream<Uint8Array>,
174
240
  _signal: AbortSignal,
175
241
  requestModel: string,
242
+ sessionId?: string,
243
+ messages?: readonly CanonicalMessage[],
176
244
  ): AsyncIterable<CanonicalEvent> {
177
245
  let seq = 1;
178
246
  yield { type: "response_start", sequence_number: seq++, model: requestModel } as CanonicalEvent;
@@ -202,15 +270,21 @@ class TraeAdapter implements ProviderAdapter {
202
270
  }
203
271
  if (frame.event === "error") {
204
272
  const data = frame.data as { message?: unknown; code?: unknown };
205
- const message = typeof data.message === "string" && data.message.length > 0
206
- ? data.message
207
- : typeof data.code === "number" || typeof data.code === "string"
208
- ? `Trae stream error (upstream code ${data.code})`
209
- : "Trae stream error";
210
- throw new GatewayError("platform_unavailable", 502, message, {}, "upstream");
273
+ throwTraeUpstreamError(data.code, data.message);
211
274
  }
212
275
  if (typeof frame.data !== "object" || frame.data === null) continue;
213
276
  const record = frame.data as Record<string, unknown>;
277
+ // The upstream can answer with an error object in the frame body rather
278
+ // than a named `error` event — e.g. HTTP 200 carrying `{"code":1001,
279
+ // "message":"…"}` (sometimes nested under an `error` key). Detect both
280
+ // spellings so a rejected turn surfaces as the real error instead of an
281
+ // empty stream that ends with a normal `stop`.
282
+ if (record["code"] !== undefined || (typeof record["error"] === "object" && record["error"] !== null)) {
283
+ const nested = record["error"] as Record<string, unknown> | undefined;
284
+ const code = record["code"] ?? nested?.["code"];
285
+ const message = record["message"] ?? nested?.["message"];
286
+ throwTraeUpstreamError(code, message);
287
+ }
214
288
  /**
215
289
  * Two frame layouts reach here and both are live.
216
290
  *
@@ -278,16 +352,25 @@ class TraeAdapter implements ProviderAdapter {
278
352
  }
279
353
  }
280
354
 
281
- if (usage !== undefined) {
282
- yield* this.#usageEvent(usage, seq++);
355
+ // Assess the raw usage exactly once per request, then reuse the result for
356
+ // both the `usage` event and the terminal. Assessing twice would let the
357
+ // terminal see the `usage` event's own history entry and misreport a cache
358
+ // hit on the very first request.
359
+ const assessed = usage === undefined ? undefined : this.#assessUsage(usage, sessionId, messages);
360
+ if (assessed !== undefined) {
361
+ yield { type: "usage", sequence_number: seq++, usage: assessed } as unknown as CanonicalEvent;
283
362
  }
284
363
  // The canonical terminal is `terminal`; a stream that ends without one is
285
- // read as "the upstream never reported a result".
364
+ // read as "the upstream never reported a result". The assessed usage is
365
+ // carried on the terminal itself (matching the other adapters) so the
366
+ // non-streaming path records the real token counts instead of falling back
367
+ // to the request estimate.
286
368
  yield {
287
369
  type: "terminal",
288
370
  sequence_number: seq,
289
371
  state: "complete",
290
372
  stop_reason: stopReason,
373
+ ...(assessed === undefined ? {} : { usage: assessed }),
291
374
  } as unknown as CanonicalEvent;
292
375
  }
293
376
 
@@ -348,20 +431,20 @@ class TraeAdapter implements ProviderAdapter {
348
431
  }
349
432
  }
350
433
 
351
- /**
352
- * Emits the terminal usage event, or none at all.
353
- *
354
- * Trae reports usage with the OpenAI spelling (`prompt_tokens` /
355
- * `completion_tokens`). `usageFromProvider` normalizes both spellings and
356
- * returns `undefined` when nothing was actually reported, and an absent
357
- * usage event is correct in that case — emitting zeros would record a
358
- * request that cost nothing.
359
- */
360
- async *#usageEvent(usage: Record<string, unknown>, seq: number): AsyncIterable<CanonicalEvent> {
434
+ /** Normalizes + cache-assesses one raw upstream usage frame, if any. */
435
+ #assessUsage(
436
+ usage: Record<string, unknown> | undefined,
437
+ sessionId?: string,
438
+ messages?: readonly CanonicalMessage[],
439
+ ): UsageRecord | undefined {
440
+ if (usage === undefined) return undefined;
361
441
  const record = usageFromProvider(usage);
362
- if (record === undefined) return;
363
- yield { type: "usage", sequence_number: seq, usage: record } as unknown as CanonicalEvent;
442
+ if (record === undefined) return undefined;
443
+ // Trae's agent surface never reports prompt-cache hits; for repeated
444
+ // prefixes we estimate them locally (see trae-cache.ts).
445
+ return estimateTraeSessionCache(record, sessionId, messages);
364
446
  }
447
+
365
448
  }
366
449
 
367
450
  export function createTraeAdapter(options: TraeAdapterOptions = {}): ProviderAdapter {