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
@@ -0,0 +1,284 @@
1
+ /**
2
+ * Manual account-status observation: refresh an OAuth token on demand and
3
+ * report the account's status AFTER the attempt — never assume it.
4
+ *
5
+ * Area 04 (D21–D26): a refresh is not a health verdict. This module is the
6
+ * explicit "refresh token" affordance for accounts the OAuth sweep excludes
7
+ * (`disabled`), and it is where the invariant nobody else had to is held:
8
+ * `statusAfter` is always re-read from the database, so no caller can ever
9
+ * report `"active"` just because a refresh succeeded.
10
+ */
11
+ import { randomUUID } from "node:crypto";
12
+ import { eq } from "drizzle-orm";
13
+ import type { CartethyiaDatabase } from "../../persistence/db-handle";
14
+ import { healthEvents, providerAccounts, providerOauthStates } from "../../persistence/schema";
15
+ import { pushStructuredConsoleLog } from "../../observability/log-ring";
16
+ import type { OAuthRefreshService, OAuthTokenRefresher } from "../authentication/oauth-refresh-service";
17
+
18
+ export type RefreshAccountStatusCode =
19
+ | "ok"
20
+ | "account_missing"
21
+ | "not_oauth"
22
+ | "static_token"
23
+ | "no_refresher"
24
+ | "refresh_failed";
25
+
26
+ export interface RefreshAccountStatusOutcome {
27
+ readonly ok: boolean;
28
+ readonly tokenRefreshed: boolean;
29
+ /**
30
+ * Account status re-read from the DB after the attempt. Never assumed from
31
+ * the input row and never `"active"` by fiat — a refresh cannot re-enable.
32
+ */
33
+ readonly statusAfter: string;
34
+ readonly code: RefreshAccountStatusCode;
35
+ /** IS0 timestamp of the fresh access-token expiry, when known. */
36
+ readonly expiresAt?: string;
37
+ /** ISO timestamp of the attempt, when it happened. */
38
+ readonly refreshedAt?: string;
39
+ /** Structured reason when `ok` is false (`not_oauth`, `static_token`, …). */
40
+ readonly error?: string;
41
+ /** Human detail (failure message) when available. */
42
+ readonly category?: string;
43
+ }
44
+
45
+ export interface RefreshAccountStatusDeps {
46
+ readonly db: CartethyiaDatabase;
47
+ readonly refreshService: Pick<OAuthRefreshService, "ensureFreshAccessToken">;
48
+ readonly resolveRefresher: (providerId: string) => Promise<OAuthTokenRefresher | undefined>;
49
+ }
50
+
51
+ interface LoadedAccount {
52
+ readonly id: string;
53
+ readonly providerId: string;
54
+ readonly credentialKind: string;
55
+ readonly status: string;
56
+ readonly staticToken: boolean;
57
+ readonly refreshCiphertext: Buffer | null;
58
+ readonly expiresAt: Date | null;
59
+ }
60
+
61
+ async function loadAccount(
62
+ db: CartethyiaDatabase,
63
+ accountId: string,
64
+ ): Promise<LoadedAccount | undefined> {
65
+ const selectBuilder = db
66
+ .select({
67
+ id: providerAccounts.id,
68
+ providerId: providerAccounts.providerId,
69
+ credentialKind: providerAccounts.credentialKind,
70
+ status: providerAccounts.status,
71
+ staticToken: providerAccounts.staticToken,
72
+ refreshCiphertext: providerOauthStates.refreshCiphertext,
73
+ expiresAt: providerOauthStates.expiresAt,
74
+ })
75
+ .from(providerAccounts);
76
+ const query =
77
+ "leftJoin" in selectBuilder
78
+ ? selectBuilder.leftJoin(
79
+ providerOauthStates,
80
+ eq(providerOauthStates.providerAccountId, providerAccounts.id),
81
+ )
82
+ : selectBuilder;
83
+ const rows = await query.where(eq(providerAccounts.id, accountId)).limit(1);
84
+ return rows[0];
85
+ }
86
+
87
+ async function readStatusAfter(
88
+ db: CartethyiaDatabase,
89
+ accountId: string,
90
+ ): Promise<{ status: string; expiresAt: Date | null } | undefined> {
91
+ const selectBuilder = db
92
+ .select({
93
+ status: providerAccounts.status,
94
+ expiresAt: providerOauthStates.expiresAt,
95
+ })
96
+ .from(providerAccounts);
97
+ const query =
98
+ "leftJoin" in selectBuilder
99
+ ? selectBuilder.leftJoin(
100
+ providerOauthStates,
101
+ eq(providerOauthStates.providerAccountId, providerAccounts.id),
102
+ )
103
+ : selectBuilder;
104
+ const rows = await query.where(eq(providerAccounts.id, accountId)).limit(1);
105
+ return rows[0];
106
+ }
107
+
108
+ /** Non-mutating health event: records the attempt, never changes status. */
109
+ async function writeRefreshHealthEvent(
110
+ db: CartethyiaDatabase,
111
+ accountId: string,
112
+ fromStatus: string,
113
+ toStatus: string,
114
+ reason: string,
115
+ ): Promise<void> {
116
+ if (typeof db.insert !== "function") return;
117
+ await db.insert(healthEvents).values({
118
+ id: randomUUID(),
119
+ entityKind: "account",
120
+ accountId,
121
+ fromStatus: fromStatus as "active" | "cooldown" | "disabled" | null,
122
+ toStatus: toStatus as "active" | "cooldown" | "disabled",
123
+ reason,
124
+ errorCategory: null,
125
+ createdAt: new Date(),
126
+ });
127
+ }
128
+
129
+ /**
130
+ * Refreshes an OAuth account's token on demand and reports the resulting
131
+ * status. Works for any status, `disabled` included — the account stays
132
+ * `disabled`; only an operator can re-enable it.
133
+ *
134
+ * Rejections are structured (`{ ok: false, code: "not_oauth" | "static_token" |
135
+ * ... }`), and every outcome carries `statusAfter` re-read from the DB.
136
+ */
137
+ export async function refreshAccountStatus(
138
+ deps: RefreshAccountStatusDeps,
139
+ accountId: string,
140
+ opts: { readonly origin?: "ui" | "sweep" | "dispatch" } = {},
141
+ ): Promise<RefreshAccountStatusOutcome> {
142
+ const origin = opts.origin ?? "ui";
143
+ const account = await loadAccount(deps.db, accountId);
144
+ if (!account) {
145
+ return {
146
+ ok: false,
147
+ tokenRefreshed: false,
148
+ statusAfter: "disabled",
149
+ code: "account_missing",
150
+ error: "Account not found",
151
+ };
152
+ }
153
+ const statusBefore = account.status;
154
+
155
+ if (account.credentialKind !== "oauth") {
156
+ return {
157
+ ok: false,
158
+ tokenRefreshed: false,
159
+ statusAfter: statusBefore,
160
+ code: "not_oauth",
161
+ error: "not_oauth",
162
+ };
163
+ }
164
+ if (account.staticToken || account.refreshCiphertext == null) {
165
+ // No refresh grant (or an explicit static marker): there is nothing to
166
+ // re-mint, and forcing the service to try would mutate the row into a
167
+ // static token. Refuse instead of changing the account.
168
+ return {
169
+ ok: false,
170
+ tokenRefreshed: false,
171
+ statusAfter: statusBefore,
172
+ code: "static_token",
173
+ error: "static_token",
174
+ ...(account.refreshCiphertext == null && !account.staticToken
175
+ ? { category: "No refresh token on record" }
176
+ : {}),
177
+ };
178
+ }
179
+
180
+ const refresher = await deps.resolveRefresher(account.providerId);
181
+ if (!refresher) {
182
+ return {
183
+ ok: false,
184
+ tokenRefreshed: false,
185
+ statusAfter: statusBefore,
186
+ code: "no_refresher",
187
+ error: "no_refresher",
188
+ };
189
+ }
190
+
191
+ // `ensureFreshAccessToken` deliberately swallows a failing refresher and
192
+ // returns null (it must not break a dispatch). For this explicit affordance
193
+ // the operator needs the reason, so keep the last failure message here
194
+ // rather than generalizing it — same "message as-is" rule as area 08.
195
+ let refreshFailureMessage: string | undefined;
196
+ const reportingRefresher: OAuthTokenRefresher = {
197
+ refresh: async (...args) => {
198
+ try {
199
+ return await refresher.refresh(...args);
200
+ } catch (error) {
201
+ refreshFailureMessage = error instanceof Error ? error.message : String(error);
202
+ throw error;
203
+ }
204
+ },
205
+ };
206
+
207
+ const startedAt = new Date();
208
+ try {
209
+ const refreshed = await deps.refreshService.ensureFreshAccessToken(accountId, reportingRefresher, {
210
+ force: true,
211
+ origin,
212
+ });
213
+ // D25: status is always re-read after the attempt — never assumed.
214
+ const after = await readStatusAfter(deps.db, accountId);
215
+ const statusAfter = after?.status ?? statusBefore;
216
+ if (refreshed !== null) {
217
+ pushStructuredConsoleLog("info", "OAuth token refreshed", {
218
+ event: "token_refresh",
219
+ accountId,
220
+ providerId: account.providerId,
221
+ details: {
222
+ origin,
223
+ statusBefore,
224
+ statusAfter,
225
+ expiresAt: after?.expiresAt?.toISOString() ?? "",
226
+ },
227
+ });
228
+ await writeRefreshHealthEvent(
229
+ deps.db,
230
+ accountId,
231
+ statusBefore,
232
+ statusAfter,
233
+ "Token refreshed — status unchanged",
234
+ );
235
+ return {
236
+ ok: true,
237
+ tokenRefreshed: true,
238
+ statusAfter,
239
+ code: "ok",
240
+ ...(after?.expiresAt ? { expiresAt: after.expiresAt.toISOString() } : {}),
241
+ refreshedAt: startedAt.toISOString(),
242
+ };
243
+ }
244
+ // `ensureFreshAccessToken` returned null: the refresh failed (transient
245
+ // upstream error, lease loss) or the credential became static mid-flight.
246
+ // Either way the account's own status is the truth, re-read above.
247
+ pushStructuredConsoleLog("warn", "OAuth token refresh did not complete", {
248
+ event: "token_refresh",
249
+ accountId,
250
+ providerId: account.providerId,
251
+ errorCode: "refresh_failed",
252
+ details: { origin, statusBefore, statusAfter },
253
+ });
254
+ return {
255
+ ok: false,
256
+ tokenRefreshed: false,
257
+ statusAfter,
258
+ code: "refresh_failed",
259
+ error: "refresh_failed",
260
+ ...(refreshFailureMessage
261
+ ? { category: refreshFailureMessage.slice(0, 500) }
262
+ : { category: "Token refresh did not complete" }),
263
+ };
264
+ } catch (error) {
265
+ const after = await readStatusAfter(deps.db, accountId);
266
+ const statusAfter = after?.status ?? statusBefore;
267
+ const message = error instanceof Error ? error.message : String(error);
268
+ pushStructuredConsoleLog("error", "OAuth token refresh failed", {
269
+ event: "token_refresh",
270
+ accountId,
271
+ providerId: account.providerId,
272
+ errorCode: "refresh_failed",
273
+ details: { origin, statusBefore, statusAfter, error: message.slice(0, 500) },
274
+ });
275
+ return {
276
+ ok: false,
277
+ tokenRefreshed: false,
278
+ statusAfter,
279
+ code: "refresh_failed",
280
+ error: "refresh_failed",
281
+ category: message.slice(0, 500),
282
+ };
283
+ }
284
+ }
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Account tier derivation from the plan string the quota collector reported.
3
+ *
4
+ * The plan string (`provider_accounts.quota_plan`) is a raw, per-provider
5
+ * label; this module normalizes it to the three-tier taxonomy the cooldown
6
+ * rules consume (`free | paid | pro | null`, D14). It is a pure function of
7
+ * the provider id and the plan text — no database access, no state.
8
+ *
9
+ * The mapping is deliberately conservative, following the same posture as
10
+ * `plan-gate.ts`: a tier is only claimed when the string proves it. "free"
11
+ * requires a free marker in the string, "pro" requires a team/enterprise/org
12
+ * signal, and "paid" is only assigned for providers whose plan strings are
13
+ * evidenced (Trae, the buddy family, Cursor). Everything else — an unknown
14
+ * provider, an unrecognized plan, a `null` plan — resolves to `null`, which
15
+ * keeps the legacy behavior intact (unknown tier ⇒ no rule applies ⇒ the old
16
+ * constants drive the cooldown).
17
+ */
18
+
19
+ /** The three-tier taxonomy for cooldown rules. `null` = unknown. */
20
+ export type AccountTier = "free" | "paid" | "pro" | null;
21
+
22
+ const TRAE_PROVIDER_IDS: ReadonlySet<string> = new Set(["trae", "traecn"]);
23
+ const BUDDY_PROVIDER_IDS: ReadonlySet<string> = new Set([
24
+ "cb",
25
+ "cbcn",
26
+ ]);
27
+ const QODER_PROVIDER_IDS: ReadonlySet<string> = new Set(["qoder", "qodercn"]);
28
+
29
+ /** Team/enterprise/org signals that prove a "pro" tier. */
30
+ const PRO_PLAN_MARKERS: readonly string[] = ["team", "enterprise", "org", "pro"];
31
+
32
+ /**
33
+ * Maps a provider's plan string to a tier.
34
+ *
35
+ * `providerId` is normalized case-insensitively; a `null`/empty plan is
36
+ * unknown (`null`). The "free" check runs before any provider branch because a
37
+ * proven-free label is the strongest signal (e.g. CodeBuddy's
38
+ * `personal_free_user`, Trae's `"Free Plan Subscription"`).
39
+ */
40
+ export function mapPlanToTier(providerId: string, plan: string | null): AccountTier {
41
+ const lower = (plan ?? "").trim().toLowerCase();
42
+ if (lower.length === 0) return null;
43
+ // Free is only claimed when the string proves it — the same marker set the
44
+ // routing plan-gate uses, so the two cannot disagree about an account.
45
+ if (lower.includes("free")) return "free";
46
+
47
+ if (QODER_PROVIDER_IDS.has(providerId.toLowerCase())) {
48
+ // Qoder's `userType` is `personal_standard` by default; team/enterprise/
49
+ // org suffixes prove a higher tier. `personal_standard` itself is not
50
+ // verified against the taxonomy, so it stays unknown rather than guessed.
51
+ if (PRO_PLAN_MARKERS.some((marker) => lower.includes(marker))) return "pro";
52
+ return null;
53
+ }
54
+
55
+ if (BUDDY_PROVIDER_IDS.has(providerId.toLowerCase())) {
56
+ if (lower.includes("enterprise") || lower.includes("team")) return "pro";
57
+ return "paid";
58
+ }
59
+
60
+ // Trae (paid plans other than the free subscription) and Cursor (a non-free
61
+ // `membershipType`/`planName` is a paying plan by construction).
62
+ if (TRAE_PROVIDER_IDS.has(providerId.toLowerCase())) return "paid";
63
+ if (providerId.toLowerCase() === "cursor") return "paid";
64
+
65
+ // Unknown provider: no claim. Legacy behavior applies.
66
+ return null;
67
+ }
@@ -77,7 +77,7 @@ async function clineClientVersion(response: Response): Promise<string | null> {
77
77
  * spells it.
78
78
  *
79
79
  * Cursor's `x-cursor-client-version` is not the IDE semver on its own: the wire
80
- * value is `cli-<version>-<sha7>` (observed live as `cli-2026.10.19-6bbf2c4`).
80
+ * value is `cli-<version>-<sha7>` (observed live as `cli-2026.11.05-86047df`).
81
81
  * The download API is the only source that returns both halves — `version` and
82
82
  * `commitSha` — so they are joined here rather than guessed. A semver alone
83
83
  * would be a different identity than the one the IDE sends.
@@ -109,18 +109,6 @@ async function kimiVersion(response: Response): Promise<string | null> {
109
109
  return isSemverish(data.info?.version) ? data.info.version.trim() : null;
110
110
  }
111
111
 
112
- async function workbuddyDesktopVersion(response: Response): Promise<string | null> {
113
- try {
114
- const data = (await response.json()) as { version?: unknown; productVersion?: unknown };
115
- const candidate = data.productVersion ?? data.version;
116
- return typeof candidate === "string" && /^\d+\.\d+\.\d+(?:\.\d+)?(?:[-+][\w.-]+)?$/.test(candidate.trim())
117
- ? candidate.trim()
118
- : null;
119
- } catch {
120
- return null;
121
- }
122
- }
123
-
124
112
  async function kiroCaskVersion(response: Response): Promise<string | null> {
125
113
  try {
126
114
  const data = (await response.json()) as { version?: unknown };
@@ -232,7 +220,7 @@ const KIRO_NODE_VERSION = "24.18.0";
232
220
  export const VERSION_SOURCES = {
233
221
  antigravity: {
234
222
  key: "antigravity",
235
- fallback: "2.28.2",
223
+ fallback: "2.29.0",
236
224
  sources: [
237
225
  {
238
226
  url: "https://antigravity-hub-auto-updater-974169037036.us-central1.run.app/manifest/latest-arm64-mac.yml",
@@ -261,21 +249,21 @@ export const VERSION_SOURCES = {
261
249
  // under `@opencode/cli`, and this feed is what its own installer reads.
262
250
  // Fallback = the current latest release; raise it whenever the package
263
251
  // moves so a cold process does not present a stale client build.
264
- fallback: "2.2.5",
252
+ fallback: "2.2.6",
265
253
  minVersion: "2.0.0",
266
254
  sources: [{ url: "https://opencode.ai/update/api/latest/cli/npm" }],
267
255
  },
268
256
  commandcode: {
269
257
  key: "commandcode",
270
- // npm `command-code` latest as of 2026-10-30. The CLI sends this as
258
+ // npm `command-code` latest as of 2026-11-07. The CLI sends this as
271
259
  // `x-command-code-version`, so a stale fallback presents a client build
272
260
  // the upstream no longer ships — raise it whenever the package moves.
273
- fallback: "1.88.2",
261
+ fallback: "1.88.5",
274
262
  sources: [{ url: "https://registry.npmjs.org/command-code/latest" }],
275
263
  },
276
264
  grok: {
277
265
  key: "grok",
278
- fallback: "1.0.70",
266
+ fallback: "1.0.77",
279
267
  sources: [
280
268
  { url: "https://storage.googleapis.com/grok-build-public-artifacts/cli/stable" },
281
269
  { url: "https://registry.npmjs.org/@xai-official/grok/latest" },
@@ -304,45 +292,9 @@ export const VERSION_SOURCES = {
304
292
  },
305
293
  codex: {
306
294
  key: "codex",
307
- fallback: "0.166.0",
295
+ fallback: "0.167.0",
308
296
  sources: [{ url: "https://registry.npmjs.org/@openai/codex/latest" }],
309
297
  },
310
- workbuddyClient: {
311
- key: "workbuddy-client",
312
- fallback: "5.9.4.42593613",
313
- sources: [
314
- {
315
- url: "https://www.workbuddy.ai/v2/update?platform=workbuddy-win32-x64-user",
316
- extract: workbuddyDesktopVersion,
317
- },
318
- {
319
- url: "https://www.workbuddy.ai/v2/update?platform=workbuddy-darwin-arm64",
320
- extract: workbuddyDesktopVersion,
321
- },
322
- ],
323
- },
324
- workbuddyCnClient: {
325
- key: "workbuddy-cn-client",
326
- fallback: "5.9.3.42398134",
327
- sources: [
328
- {
329
- url: "https://www.workbuddy.cn/v2/update?platform=workbuddy-win32-x64-user",
330
- extract: workbuddyDesktopVersion,
331
- },
332
- {
333
- url: "https://www.workbuddy.cn/v2/update?platform=workbuddy-darwin-arm64",
334
- extract: workbuddyDesktopVersion,
335
- },
336
- ],
337
- },
338
- workbuddyCli: {
339
- key: "workbuddy",
340
- fallback: "2.178.1",
341
- sources: [
342
- { url: "https://registry.npmjs.org/@tencent-ai/codebuddy-code/latest" },
343
- { url: "https://registry.npmmirror.com/@tencent-ai/codebuddy-code/latest" },
344
- ],
345
- },
346
298
  kimiCli: {
347
299
  key: "kimi-cli",
348
300
  fallback: "1.52.0",
@@ -350,7 +302,7 @@ export const VERSION_SOURCES = {
350
302
  },
351
303
  kiro: {
352
304
  key: "kiro",
353
- fallback: "1.4.62",
305
+ fallback: "1.4.121",
354
306
  sources: [
355
307
  {
356
308
  // Kiro's downloads page exposes no stable version string. Homebrew
@@ -364,7 +316,7 @@ export const VERSION_SOURCES = {
364
316
  },
365
317
  codebuddy: {
366
318
  key: "codebuddy",
367
- fallback: "2.178.1",
319
+ fallback: "2.181.0",
368
320
  sources: [
369
321
  { url: "https://registry.npmjs.org/@tencent-ai/codebuddy-code/latest" },
370
322
  { url: "https://registry.npmmirror.com/@tencent-ai/codebuddy-code/latest" },
@@ -372,7 +324,7 @@ export const VERSION_SOURCES = {
372
324
  },
373
325
  codebuddyCn: {
374
326
  key: "codebuddy-cn",
375
- fallback: "4.12.2.41207131",
327
+ fallback: "4.12.3.42658816",
376
328
  sources: [
377
329
  {
378
330
  // Official CN builds are listed by Homebrew's maintained cask feed;
@@ -387,7 +339,7 @@ export const VERSION_SOURCES = {
387
339
  // Current `@anthropic-ai/claude-code` release, so the billing
388
340
  // `cc_version=` suffix and the `claude-cli/` User-Agent stay on what
389
341
  // upstream ships.
390
- fallback: "2.1.314",
342
+ fallback: "2.1.319",
391
343
  sources: [{ url: "https://registry.npmjs.org/@anthropic-ai/claude-code/latest" }],
392
344
  },
393
345
  claudeSdk: {
@@ -404,7 +356,7 @@ export const VERSION_SOURCES = {
404
356
  },
405
357
  github: {
406
358
  key: "github-copilot-chat",
407
- fallback: "0.43.0",
359
+ fallback: "0.43.2026040705",
408
360
  sources: [
409
361
  {
410
362
  url: "https://api.github.com/repos/microsoft/vscode-copilot-chat/releases/latest",
@@ -414,7 +366,7 @@ export const VERSION_SOURCES = {
414
366
  },
415
367
  kilo: {
416
368
  key: "kilo-code",
417
- fallback: "7.8.25",
369
+ fallback: "7.8.27",
418
370
  sources: [{ url: "https://open-vsx.org/api/kilocode/kilo-code" }],
419
371
  },
420
372
  muse: {
@@ -436,12 +388,27 @@ export const VERSION_SOURCES = {
436
388
  },
437
389
  ],
438
390
  },
391
+ freepi: {
392
+ key: "freepi",
393
+ // The free-pi CLI version — the `x-client-version` the gateway reports.
394
+ // Fallback = the unpacked 0.6.14 bundle; npm discovery replaces it.
395
+ fallback: "0.6.14",
396
+ sources: [{ url: "https://registry.npmjs.org/free-pi-cli/latest" }],
397
+ },
398
+ perch: {
399
+ key: "perch",
400
+ // The official Perch CLI, spawned by the adapter for hosted inference.
401
+ fallback: "2.4.125",
402
+ sources: [{ url: "https://registry.npmjs.org/perchai-cli/latest" }],
403
+ },
439
404
  cursor: {
440
405
  key: "cursor",
441
- // Fallback = the last observed wire version. Discovery replaces it within
442
- // one monitor tick; it is only what a cold process sends before the first
443
- // successful fetch.
444
- fallback: "cli-3.27.22-b1aa50c",
406
+ // Fallback = the last observed wire version (CLI ~2026.11.07-306d153).
407
+ // Discovery replaces it within one monitor tick; it is only what a cold
408
+ // process sends before the first successful fetch. Kept equal to
409
+ // `FALLBACK_CLIENT_VERSION` in the cursor adapter so the badge and the
410
+ // request header always send one identity.
411
+ fallback: "cli-3.29.9-306d153",
445
412
  sources: [
446
413
  {
447
414
  url: "https://cursor.com/api/download?platform=win32-x64-user&releaseTrack=stable",
@@ -455,7 +422,7 @@ export const VERSION_SOURCES = {
455
422
  // comment this replaced claimed no semver existed and pinned a date tag
456
423
  // (`v2026.9.24`) as the version — a tag, not a release, and not the
457
424
  // identity the client sends.
458
- fallback: "0.22.5",
425
+ fallback: "0.23.0",
459
426
  sources: [
460
427
  {
461
428
  url: "https://api.github.com/repos/NousResearch/hermes-agent/releases/latest",
@@ -465,7 +432,7 @@ export const VERSION_SOURCES = {
465
432
  },
466
433
  zcode: {
467
434
  key: "zcode",
468
- fallback: "3.14.3",
435
+ fallback: "3.17.2",
469
436
  sources: [
470
437
  {
471
438
  url: "https://api.github.com/repos/zai-org/ZCode/releases/latest",
@@ -480,6 +447,8 @@ const resolvers = {
480
447
  qoder: createClientVersionResolver({ ...VERSION_SOURCES.qoder, client: "qoder" }),
481
448
  hermes: createClientVersionResolver({ ...VERSION_SOURCES.hermes, client: "hermes" }),
482
449
  trae: createClientVersionResolver({ ...VERSION_SOURCES.trae, client: "trae" }),
450
+ freepi: createClientVersionResolver({ ...VERSION_SOURCES.freepi, client: "freepi" }),
451
+ perch: createClientVersionResolver({ ...VERSION_SOURCES.perch, client: "perch" }),
483
452
  cursor: createClientVersionResolver({ ...VERSION_SOURCES.cursor, client: "cursor" }),
484
453
  opencode: createClientVersionResolver({ ...VERSION_SOURCES.opencode, client: "opencode" }),
485
454
  commandcode: createClientVersionResolver({ ...VERSION_SOURCES.commandcode, client: "command-code" }),
@@ -488,9 +457,6 @@ const resolvers = {
488
457
  clineSdk: createClientVersionResolver({ ...VERSION_SOURCES.clineSdk, client: "cline-sdk" }),
489
458
  clineCli: createClientVersionResolver({ ...VERSION_SOURCES.clineCli, client: "cline-cli" }),
490
459
  codex: createClientVersionResolver({ ...VERSION_SOURCES.codex, client: "codex" }),
491
- workbuddyClient: createClientVersionResolver({ ...VERSION_SOURCES.workbuddyClient, client: "workbuddy" }),
492
- workbuddyCnClient: createClientVersionResolver({ ...VERSION_SOURCES.workbuddyCnClient, client: "workbuddy-cn" }),
493
- workbuddyCli: createClientVersionResolver({ ...VERSION_SOURCES.workbuddyCli, client: "codebuddy" }),
494
460
  kimiCli: createClientVersionResolver({ ...VERSION_SOURCES.kimiCli, client: "kimi" }),
495
461
  codebuddy: createClientVersionResolver({ ...VERSION_SOURCES.codebuddy, client: "codebuddy" }),
496
462
  codebuddyCn: createClientVersionResolver({ ...VERSION_SOURCES.codebuddyCn, client: "codebuddy" }),
@@ -520,8 +486,6 @@ const PROVIDER_VERSION_RESOLVERS: Readonly<Record<string, ClientVersionResolver>
520
486
  traecn: resolvers.trae,
521
487
  cursor: resolvers.cursor,
522
488
  commandcode: resolvers.commandcode,
523
- workbuddy: resolvers.workbuddyClient,
524
- workbuddycn: resolvers.workbuddyCnClient,
525
489
  cb: resolvers.codebuddy,
526
490
  cbcn: resolvers.codebuddyCn,
527
491
  kimi: resolvers.kimiCli,
@@ -641,37 +605,11 @@ export const _resetClineCliVersionCache = accessor("clineCli").reset;
641
605
 
642
606
  export const getCodexVersion = accessor("codex").get;
643
607
 
644
- export const getWorkBuddyClientVersion = accessor("workbuddyClient").get;
645
- export const getWorkBuddyCnClientVersion = accessor("workbuddyCnClient").get;
646
- export const getWorkBuddyCliVersion = accessor("workbuddyCli").get;
647
-
648
- export function buildWorkBuddyUserAgent(
649
- identity = "WB",
650
- clientVersion = getWorkBuddyClientVersion(),
651
- cliVersion = getWorkBuddyCliVersion(),
652
- ): string {
653
- // 1:1 with codebuddy2api. The CLI profile is `CLI/<v> CodeBuddy/<v>` and the
654
- // WorkBuddy profile is `WorkBuddy/<wb> workbuddy-ai/<wb> CLI/<cli>`, which
655
- // sends NO SDK headers — two separate profiles, not one string with a switch.
656
- // Whether SDK headers ride along is decided by `workbuddyHeaders`, not here, so
657
- // this builder only decides the UA text.
658
- //
659
- // The second token is the wire spelling `workbuddy-ai`, NOT the display
660
- // brand `WorkBuddy AI`. This is what an official client actually sends; the
661
- // product is branded with a space in the UI and in docs, and copying that
662
- // branding into the header produced a UA no real client emits — which the
663
- // intl gateway answers with 403 `11140`, the same refusal it gives a
664
- // wrong-brand header. Product name ≠ wire token.
665
- if (identity === "CLI") return `CLI/${cliVersion} CodeBuddy/${cliVersion}`;
666
- return `WorkBuddy/${clientVersion} workbuddy-ai/${clientVersion} CLI/${cliVersion}`;
667
- }
668
-
669
- export const _resetWorkBuddyCnClientVersionCache = accessor("workbuddyCnClient").reset;
670
-
671
608
  export const getKimiCliVersion = accessor("kimiCli").get;
672
609
 
673
610
  export const getCodeBuddyVersion = accessor("codebuddy").get;
674
611
  export const getCodeBuddyCnVersion = accessor("codebuddyCn").get;
612
+
675
613
  /**
676
614
  * The CodeBuddy User-Agent.
677
615
  *
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Static in-code registry of provider × category × tier cooldown rules (D19).
3
+ *
4
+ * The registry ships empty by default: before a rule is filled in, behavior is
5
+ * byte-identical to the old hardcoded constants. Rules are keyed by
6
+ * `(providerId, category, tier)` and, when present, replace the provider's old
7
+ * cooldown constant for that combination — they sit between the provider-stated
8
+ * window (which always wins, D18) and the old constant/env fallback.
9
+ *
10
+ * The initial content is the example from the approved proposal (part 03):
11
+ * Trae's daily-quota verdict (`model_unreachable`, upstream 4008) cools a free
12
+ * account for a full day and a paid/pro account for 15 minutes (D17 — `paid`
13
+ * never gets `0`, which would hammer the upstream on a wrong verdict).
14
+ */
15
+ import type { AccountErrorCategory } from "./account-error-classification";
16
+ import type { AccountTier } from "./account-tier";
17
+
18
+ export type CooldownRuleKey = {
19
+ readonly providerId: string;
20
+ readonly category: AccountErrorCategory;
21
+ readonly tier: AccountTier;
22
+ };
23
+
24
+ type TierRules = Partial<Record<NonNullable<AccountTier>, number>>;
25
+ type CategoryRules = Partial<Record<AccountErrorCategory, TierRules>>;
26
+ type ProviderRules = Record<string, CategoryRules>;
27
+
28
+ const DAY = 24 * 60 * 60 * 1000;
29
+ const MINUTE = 60 * 1000;
30
+
31
+ const COOLDOWN_RULES: ProviderRules = {
32
+ trae: {
33
+ model_unreachable: {
34
+ free: DAY,
35
+ paid: 15 * MINUTE,
36
+ pro: 15 * MINUTE,
37
+ },
38
+ },
39
+ // Trae CN is the same upstream product under a second realm id — the
40
+ // classifier already treats the two identically (trae-region-unavailable,
41
+ // trae-quota-exhausted), so the rules mirror.
42
+ traecn: {
43
+ model_unreachable: {
44
+ free: DAY,
45
+ paid: 15 * MINUTE,
46
+ pro: 15 * MINUTE,
47
+ },
48
+ },
49
+ };
50
+
51
+ /**
52
+ * Resolves a cooldown rule for `(providerId, category, tier)`.
53
+ *
54
+ * Returns `undefined` when no rule exists — for an unknown provider, an
55
+ * unstored category, or an unknown tier — which is the signal to keep the old
56
+ * constant/fallback behavior.
57
+ */
58
+ export function resolveCooldownRule(key: CooldownRuleKey): number | undefined {
59
+ if (key.tier === null || key.tier === undefined) return undefined;
60
+ return COOLDOWN_RULES[key.providerId.toLowerCase()]?.[key.category]?.[key.tier];
61
+ }