cartethyia 2.6.7 → 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 (335) hide show
  1. package/.env.example +1 -7
  2. package/README.md +25 -2
  3. package/dist/dashboard/assets/{BannedPage-Dskhyp0l.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--SJhk7qQ.js → CliToolDetailPage-CVe6RaBH.js} +1 -1
  6. package/dist/dashboard/assets/CliToolDetailPage-CVe6RaBH.js.gz +0 -0
  7. package/dist/dashboard/assets/{CliToolsPage-C203VT-c.js → CliToolsPage-Dd8ZSamp.js} +1 -1
  8. package/dist/dashboard/assets/CliToolsPage-Dd8ZSamp.js.gz +0 -0
  9. package/dist/dashboard/assets/{CombosPage-BcS7kGG1.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-uujbBYwC.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-CA1kLs45.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-BueSWX5D.js → QuotaPage-ATpaxrbl.js} +1 -1
  32. package/dist/dashboard/assets/QuotaPage-ATpaxrbl.js.gz +0 -0
  33. package/dist/dashboard/assets/SettingsPage-CETophpx.js +1 -0
  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-DrKeMcGN.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-qjuYtccc.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-C23S9sgp.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-DU6qrMA_.js → assert-67xXH5rf.js} +1 -1
  48. package/dist/dashboard/assets/assert-67xXH5rf.js.gz +0 -0
  49. package/dist/dashboard/assets/{badge-Bkz_-RrM.js → badge-DC2L43Mj.js} +1 -1
  50. package/dist/dashboard/assets/badge-DC2L43Mj.js.gz +0 -0
  51. package/dist/dashboard/assets/{banner-C-7y3VKV.js → banner-BBVcb0J3.js} +1 -1
  52. package/dist/dashboard/assets/banner-BBVcb0J3.js.gz +0 -0
  53. package/dist/dashboard/assets/{card-BOPBuBzD.js → card-Cca1XC6G.js} +1 -1
  54. package/dist/dashboard/assets/card-Cca1XC6G.js.gz +0 -0
  55. package/dist/dashboard/assets/{cli-tools-BaFUuFFF.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-e-Mlk0FH.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-Iec_ywbD.js → dialog-DFvWhwTa.js} +1 -1
  60. package/dist/dashboard/assets/dialog-DFvWhwTa.js.gz +0 -0
  61. package/dist/dashboard/assets/{drawer-Bs8MIIHc.js → drawer-CFM2sMX1.js} +1 -1
  62. package/dist/dashboard/assets/drawer-CFM2sMX1.js.gz +0 -0
  63. package/dist/dashboard/assets/{entry-DOddwoc0.js → entry-BLBLxX0h.js} +3 -3
  64. package/dist/dashboard/assets/entry-BLBLxX0h.js.gz +0 -0
  65. package/dist/dashboard/assets/{entry-DcobaKFu.js → entry-Bqy8RFvM.js} +1 -1
  66. package/dist/dashboard/assets/entry-Bqy8RFvM.js.gz +0 -0
  67. package/dist/dashboard/assets/{entry-BRszm4_Z.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-BuDsrsPn.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-BeLRhsmt.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-DToMICwl.js → inline-BFUcvllj.js} +1 -1
  76. package/dist/dashboard/assets/inline-BFUcvllj.js.gz +0 -0
  77. package/dist/dashboard/assets/{input-WlyHWeG2.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-DaJ5baJr.js → layout-CBIyT6Rt.js} +1 -1
  80. package/dist/dashboard/assets/layout-CBIyT6Rt.js.gz +0 -0
  81. package/dist/dashboard/assets/{list-row-B3oSK13z.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-ta6GMb-8.js → live-D0mm--ME.js} +1 -1
  84. package/dist/dashboard/assets/live-D0mm--ME.js.gz +0 -0
  85. package/dist/dashboard/assets/minimize-2-D-MN8W8a.js +1 -0
  86. package/dist/dashboard/assets/minimize-2-D-MN8W8a.js.gz +0 -0
  87. package/dist/dashboard/assets/{mutation-g_fduiNw.js → mutation-sMD965oO.js} +1 -1
  88. package/dist/dashboard/assets/mutation-sMD965oO.js.gz +0 -0
  89. package/dist/dashboard/assets/{numbers-Dulqgys5.js → numbers-BO9dKaFr.js} +1 -1
  90. package/dist/dashboard/assets/numbers-BO9dKaFr.js.gz +0 -0
  91. package/dist/dashboard/assets/providers/inception.svg +5 -0
  92. package/dist/dashboard/assets/providers/inception.svg.gz +0 -0
  93. package/dist/dashboard/assets/{query-keys-UETqfytp.js → query-keys-CfL_Zdoh.js} +1 -1
  94. package/dist/dashboard/assets/query-keys-CfL_Zdoh.js.gz +0 -0
  95. package/dist/dashboard/assets/{routing-BPUo-W8d.js → routing-BnIpi1IJ.js} +1 -1
  96. package/dist/dashboard/assets/routing-BnIpi1IJ.js.gz +0 -0
  97. package/dist/dashboard/assets/{select-BfZ3SCTM.js → select-Cj5Nw43g.js} +1 -1
  98. package/dist/dashboard/assets/select-Cj5Nw43g.js.gz +0 -0
  99. package/dist/dashboard/assets/settings-BAFhDgX8.js +1 -0
  100. package/dist/dashboard/assets/settings-BAFhDgX8.js.gz +0 -0
  101. package/dist/dashboard/assets/{stack-B1w5BM0u.js → stack-EMrOMt4I.js} +1 -1
  102. package/dist/dashboard/assets/stack-EMrOMt4I.js.gz +0 -0
  103. package/dist/dashboard/assets/{state-DhZBfWgx.js → state-CWoo7ty5.js} +1 -1
  104. package/dist/dashboard/assets/state-CWoo7ty5.js.gz +0 -0
  105. package/dist/dashboard/assets/{switch-yD7D99Lg.js → switch-CCjT6IkF.js} +1 -1
  106. package/dist/dashboard/assets/switch-CCjT6IkF.js.gz +0 -0
  107. package/dist/dashboard/assets/{useMutation-EDFRkn1i.js → useMutation-DjkLHUcE.js} +1 -1
  108. package/dist/dashboard/assets/useMutation-DjkLHUcE.js.gz +0 -0
  109. package/dist/dashboard/index.html +1 -1
  110. package/dist/dashboard/index.html.gz +0 -0
  111. package/dist/dashboard/precompressed-assets.json +55 -54
  112. package/migrations/full-postgres/0024_add_pool_provider_cooldowns.sql +12 -0
  113. package/migrations/full-postgres/0025_add_pool_random_strategy.sql +3 -0
  114. package/migrations/lite-sqlite3/0024_add_pool_provider_cooldowns.sql +12 -0
  115. package/migrations/lite-sqlite3/0025_add_pool_random_strategy.sql +20 -0
  116. package/package.json +1 -1
  117. package/scripts/cli/console-menu.ts +5 -2
  118. package/scripts/cli/tray.ts +21 -6
  119. package/src/boot/app.ts +24 -62
  120. package/src/boot/dependencies.ts +732 -750
  121. package/src/config/cooldowns.ts +16 -0
  122. package/src/config/providers.ts +13 -1
  123. package/src/config/resources.ts +13 -80
  124. package/src/config/security.ts +2 -38
  125. package/src/config/spec.ts +16 -23
  126. package/src/console/console-router.ts +7 -7
  127. package/src/console/domain-registration.ts +19 -12
  128. package/src/console/providers/catalog/catalog-projections.ts +2 -6
  129. package/src/console/providers/catalog/contracts.ts +19 -0
  130. package/src/console/providers/catalog/provider-operations.ts +38 -0
  131. package/src/console/providers/catalog/routes.ts +14 -0
  132. package/src/console/providers/catalog/store-helpers.ts +326 -0
  133. package/src/console/providers/catalog/store.ts +1292 -1563
  134. package/src/console/quota/account-quota-global.ts +2 -2
  135. package/src/console/quota/account-quota-shared.ts +1 -1
  136. package/src/console/quota/account-quota-tenant.ts +35 -12
  137. package/src/console/quota/quota-view.ts +2 -2
  138. package/src/console/remote-support/contracts.ts +3 -1
  139. package/src/console/remote-support/service.ts +115 -6
  140. package/src/console/routing/pools/contracts.ts +41 -1
  141. package/src/console/routing/pools/operations.ts +183 -4
  142. package/src/console/routing/pools/relay-deploy.ts +17 -4
  143. package/src/console/routing/pools/routes.ts +30 -0
  144. package/src/console/routing/pools/store.ts +98 -8
  145. package/src/main.ts +16 -2
  146. package/src/network/outbound-fetch.ts +226 -365
  147. package/src/network/pool/agent.ts +8 -0
  148. package/src/network/pool/pool-state.ts +7 -0
  149. package/src/network/pool/selector.ts +110 -2
  150. package/src/network/pool-cooldown-store.ts +252 -0
  151. package/src/network/pool-health-machine.ts +5 -3
  152. package/src/network/pool-health.ts +36 -9
  153. package/src/observability/event-query.ts +57 -0
  154. package/src/observability/metrics.ts +15 -5
  155. package/src/observability/runtime-metrics.ts +30 -15
  156. package/src/persistence/health-event-flush.ts +2 -0
  157. package/src/persistence/schema.ts +19 -0
  158. package/src/persistence/usage-flush.ts +8 -3
  159. package/src/protocol/canonical-model.ts +1 -1
  160. package/src/protocol/dsml.ts +377 -0
  161. package/src/protocol/gateway-error.ts +0 -1
  162. package/src/{providers/provider-reasoning.ts → protocol/reasoning.ts} +10 -9
  163. package/src/protocol/request/chat.ts +7 -7
  164. package/src/protocol/request/codex.ts +2 -2
  165. package/src/protocol/request/gemini.ts +1 -1
  166. package/src/protocol/request/messages.ts +6 -6
  167. package/src/protocol/request/responses.ts +7 -7
  168. package/src/protocol/response/dsml.ts +12 -133
  169. package/src/{transport → protocol}/translation/attachment-integrity.ts +2 -2
  170. package/src/{transport → protocol}/translation/cache-controls.ts +1 -1
  171. package/src/{transport → protocol}/translation/capabilities.ts +2 -2
  172. package/src/{transport → protocol}/translation/normalize.ts +1 -1
  173. package/src/{transport → protocol}/translation/quirks.ts +1 -1
  174. package/src/{transport → protocol}/translation/search.ts +1 -1
  175. package/src/{transport → protocol}/translation/thinking.ts +23 -1
  176. package/src/{transport → protocol}/translation/tool-id.ts +1 -1
  177. package/src/{transport → protocol}/translation/tool-repair.ts +2 -2
  178. package/src/protocol/upstream-parsing.ts +176 -0
  179. package/src/providers/adapters/compatible-adapter.ts +11 -2
  180. package/src/providers/authentication/oauth-refresh-service.ts +9 -5
  181. package/src/providers/default-registry.ts +21 -5
  182. package/src/providers/discovery/probe-phases.ts +16 -3
  183. package/src/providers/discovery/probing-service.ts +4 -1
  184. package/src/providers/integrations/anthropic.ts +1 -1
  185. package/src/providers/integrations/buddy/buddy-catalog-shared.ts +1 -4
  186. package/src/providers/integrations/buddy/buddy-chat-shared.ts +141 -8
  187. package/src/providers/integrations/buddy/buddy-checkin.ts +5 -1
  188. package/src/providers/integrations/buddy/buddy-growth-shared.ts +0 -82
  189. package/src/providers/integrations/buddy/buddy-identity.ts +25 -0
  190. package/src/providers/integrations/buddy/buddy-server-tools.ts +240 -0
  191. package/src/providers/integrations/buddy/codebuddy/cn-catalog.ts +36 -0
  192. package/src/providers/integrations/buddy/codebuddy/cn-provider.ts +6 -31
  193. package/src/providers/integrations/buddy/codebuddy/provider.ts +2 -0
  194. package/src/providers/integrations/buddy/codebuddy/shared.ts +19 -4
  195. package/src/providers/integrations/buddy/turing-device-token.ts +262 -0
  196. package/src/providers/integrations/buddy/workbuddy/provider.ts +2 -0
  197. package/src/providers/integrations/buddy/workbuddy/shared.ts +56 -3
  198. package/src/providers/integrations/claude/claude.ts +1 -1
  199. package/src/providers/integrations/cloudflareai/cloudflareai.ts +1 -1
  200. package/src/providers/integrations/codex/codex-errors.ts +1 -1
  201. package/src/providers/integrations/codex/codex.ts +1 -1
  202. package/src/providers/integrations/commandcode/commandcode.ts +134 -104
  203. package/src/providers/integrations/commandcode/discovery.ts +6 -5
  204. package/src/providers/integrations/factory/factory.ts +1 -1
  205. package/src/providers/integrations/github/github.ts +1 -1
  206. package/src/providers/integrations/grok/grok.ts +1 -1
  207. package/src/providers/integrations/inception/provider.ts +91 -0
  208. package/src/providers/integrations/inferhub.ts +1 -1
  209. package/src/providers/integrations/kiro/kiro-request.ts +1 -1
  210. package/src/providers/integrations/mistral.ts +193 -19
  211. package/src/providers/integrations/opencode/fingerprint.ts +19 -2
  212. package/src/providers/integrations/opencode/prompt.ts +111 -0
  213. package/src/providers/integrations/opencode/provider.ts +101 -23
  214. package/src/providers/integrations/opencode/tool-definitions.ts +4 -4
  215. package/src/providers/integrations/qoder/qoder-checkin.ts +303 -0
  216. package/src/providers/integrations/qoder/qoder.ts +1641 -884
  217. package/src/providers/integrations/shared/install-id.ts +2 -1
  218. package/src/providers/integrations/trae/trae-cache.ts +110 -0
  219. package/src/providers/integrations/trae/trae-oauth.ts +29 -4
  220. package/src/providers/integrations/trae/trae-protocol.ts +2 -1
  221. package/src/providers/integrations/trae/trae.ts +106 -23
  222. package/src/providers/operations/account-error-classification.ts +1660 -0
  223. package/src/providers/operations/account-health-service.ts +621 -2097
  224. package/src/providers/operations/account-reset-service.ts +2 -0
  225. package/src/providers/provider-contracts.ts +0 -2
  226. package/src/providers/provider-metadata.ts +25 -4
  227. package/src/providers/provider-probe.ts +2 -2
  228. package/src/{console → providers}/quota/cache.ts +1 -1
  229. package/src/{console → providers}/quota/refresh.ts +5 -5
  230. package/src/security/abuse.ts +17 -0
  231. package/src/transport/dispatch/attempt-finalize.ts +114 -12
  232. package/src/transport/dispatch/attempt-loop.ts +3 -0
  233. package/src/transport/dispatch/empty-stream.ts +224 -217
  234. package/src/transport/dispatch/generation.ts +37 -55
  235. package/src/transport/dispatch/handler-deps.ts +32 -0
  236. package/src/transport/dispatch/leases.ts +9 -4
  237. package/src/transport/dispatch/proxy-request.ts +6 -30
  238. package/src/transport/dispatch/responses-compact.ts +22 -43
  239. package/src/transport/dispatch/streaming/finalize.ts +3 -0
  240. package/src/transport/dispatch/streaming/stream.ts +0 -8
  241. package/src/transport/dispatch/systemone.ts +22 -42
  242. package/src/transport/dispatch/token-count.ts +23 -39
  243. package/src/transport/dispatch/upstream.ts +1 -3
  244. package/src/transport/dispatch/websearch.ts +22 -41
  245. package/src/transport/failure-policy.ts +59 -178
  246. package/src/transport/guards/abuse-guard.ts +10 -0
  247. package/src/transport/guards/auth-guard.ts +0 -18
  248. package/src/transport/guards/error-lifecycle.ts +1 -1
  249. package/src/transport/guards/pipeline.ts +0 -6
  250. package/src/transport/guards/request-context.ts +18 -91
  251. package/src/transport/request/degradation.ts +20 -1
  252. package/src/transport/request/inflight.ts +4 -95
  253. package/src/transport/request/preparer.ts +6 -6
  254. package/src/transport/request/state.ts +66 -38
  255. package/src/transport/request/tenant-preferences.ts +1 -1
  256. package/src/transport/routing/route-catalog.ts +41 -31
  257. package/src/transport/routing/router.ts +2 -2
  258. package/src/transport/surface/messages/parse.ts +1 -1
  259. package/src/workers/buddy-growth-tasks.ts +346 -425
  260. package/src/workers/daily-checkin.ts +17 -6
  261. package/src/workers/quota-refresh-sweep.ts +2 -2
  262. package/src/workers/scheduler.ts +17 -1
  263. package/dist/dashboard/assets/BannedPage-Dskhyp0l.js.gz +0 -0
  264. package/dist/dashboard/assets/CliToolDetailPage--SJhk7qQ.js.gz +0 -0
  265. package/dist/dashboard/assets/CliToolsPage-C203VT-c.js.gz +0 -0
  266. package/dist/dashboard/assets/CombosPage-BcS7kGG1.js.gz +0 -0
  267. package/dist/dashboard/assets/ConsoleLogPage-ChmiSLwO.js +0 -1
  268. package/dist/dashboard/assets/ConsoleLogPage-ChmiSLwO.js.gz +0 -0
  269. package/dist/dashboard/assets/CustomizationPage-uujbBYwC.js.gz +0 -0
  270. package/dist/dashboard/assets/HealthEventsModal-fKAWd-OQ.js +0 -1
  271. package/dist/dashboard/assets/HealthEventsModal-fKAWd-OQ.js.gz +0 -0
  272. package/dist/dashboard/assets/LoginPage-DoOUthbG.js +0 -1
  273. package/dist/dashboard/assets/LoginPage-DoOUthbG.js.gz +0 -0
  274. package/dist/dashboard/assets/ModelPicker-CA1kLs45.js.gz +0 -0
  275. package/dist/dashboard/assets/OverviewPage-C5Z-UBz1.js +0 -1
  276. package/dist/dashboard/assets/OverviewPage-C5Z-UBz1.js.gz +0 -0
  277. package/dist/dashboard/assets/ProviderDetailPage-CFu1qGcT.js +0 -3
  278. package/dist/dashboard/assets/ProviderDetailPage-CFu1qGcT.js.gz +0 -0
  279. package/dist/dashboard/assets/ProviderIcon-CmfzxpVM.js +0 -1
  280. package/dist/dashboard/assets/ProviderIcon-CmfzxpVM.js.gz +0 -0
  281. package/dist/dashboard/assets/ProvidersPage-CnlJZbxF.js +0 -1
  282. package/dist/dashboard/assets/ProvidersPage-CnlJZbxF.js.gz +0 -0
  283. package/dist/dashboard/assets/ProxyPage-B9mBTweh.js +0 -7
  284. package/dist/dashboard/assets/ProxyPage-B9mBTweh.js.gz +0 -0
  285. package/dist/dashboard/assets/QuotaPage-BueSWX5D.js.gz +0 -0
  286. package/dist/dashboard/assets/SettingsPage-B0x00MVY.js +0 -1
  287. package/dist/dashboard/assets/SettingsPage-B0x00MVY.js.gz +0 -0
  288. package/dist/dashboard/assets/SetupPage-DQ9oDRpi.js +0 -1
  289. package/dist/dashboard/assets/SetupPage-DQ9oDRpi.js.gz +0 -0
  290. package/dist/dashboard/assets/SortableList-DrKeMcGN.js.gz +0 -0
  291. package/dist/dashboard/assets/StudioPage-qjuYtccc.js.gz +0 -0
  292. package/dist/dashboard/assets/UsagePage-Be7aNviQ.js +0 -56
  293. package/dist/dashboard/assets/UsagePage-Be7aNviQ.js.gz +0 -0
  294. package/dist/dashboard/assets/api-keys-C23S9sgp.js.gz +0 -0
  295. package/dist/dashboard/assets/app-BUSiYL6F.css +0 -2
  296. package/dist/dashboard/assets/app-BUSiYL6F.css.gz +0 -0
  297. package/dist/dashboard/assets/assert-DU6qrMA_.js.gz +0 -0
  298. package/dist/dashboard/assets/badge-Bkz_-RrM.js.gz +0 -0
  299. package/dist/dashboard/assets/banner-C-7y3VKV.js.gz +0 -0
  300. package/dist/dashboard/assets/card-BOPBuBzD.js.gz +0 -0
  301. package/dist/dashboard/assets/cli-tools-BaFUuFFF.js.gz +0 -0
  302. package/dist/dashboard/assets/clipboard-button-e-Mlk0FH.js.gz +0 -0
  303. package/dist/dashboard/assets/cog-CdWhsajs.js +0 -1
  304. package/dist/dashboard/assets/cog-CdWhsajs.js.gz +0 -0
  305. package/dist/dashboard/assets/dialog-Iec_ywbD.js.gz +0 -0
  306. package/dist/dashboard/assets/drawer-Bs8MIIHc.js.gz +0 -0
  307. package/dist/dashboard/assets/entry-BRszm4_Z.js.gz +0 -0
  308. package/dist/dashboard/assets/entry-DOddwoc0.js.gz +0 -0
  309. package/dist/dashboard/assets/entry-DcobaKFu.js.gz +0 -0
  310. package/dist/dashboard/assets/github-badge-BuDsrsPn.js.gz +0 -0
  311. package/dist/dashboard/assets/index-BeLRhsmt.js.gz +0 -0
  312. package/dist/dashboard/assets/inline-DToMICwl.js.gz +0 -0
  313. package/dist/dashboard/assets/input-WlyHWeG2.js.gz +0 -0
  314. package/dist/dashboard/assets/layout-DaJ5baJr.js.gz +0 -0
  315. package/dist/dashboard/assets/list-row-B3oSK13z.js.gz +0 -0
  316. package/dist/dashboard/assets/live-ta6GMb-8.js.gz +0 -0
  317. package/dist/dashboard/assets/model-bans-BmYTmbAk.js +0 -1
  318. package/dist/dashboard/assets/model-bans-BmYTmbAk.js.gz +0 -0
  319. package/dist/dashboard/assets/mutation-g_fduiNw.js.gz +0 -0
  320. package/dist/dashboard/assets/numbers-Dulqgys5.js.gz +0 -0
  321. package/dist/dashboard/assets/query-keys-UETqfytp.js.gz +0 -0
  322. package/dist/dashboard/assets/routing-BPUo-W8d.js.gz +0 -0
  323. package/dist/dashboard/assets/select-BfZ3SCTM.js.gz +0 -0
  324. package/dist/dashboard/assets/settings--3hxSyry.js +0 -1
  325. package/dist/dashboard/assets/settings--3hxSyry.js.gz +0 -0
  326. package/dist/dashboard/assets/stack-B1w5BM0u.js.gz +0 -0
  327. package/dist/dashboard/assets/state-DhZBfWgx.js.gz +0 -0
  328. package/dist/dashboard/assets/switch-yD7D99Lg.js.gz +0 -0
  329. package/dist/dashboard/assets/useMutation-EDFRkn1i.js.gz +0 -0
  330. package/src/console/admin/model-abuse/contracts.ts +0 -38
  331. package/src/console/admin/model-abuse/routes.ts +0 -97
  332. package/src/security/model-abuse.ts +0 -704
  333. package/src/transport/guards/concurrency-guard.ts +0 -125
  334. /package/dist/dashboard/assets/{app-MdVfwraB.js → app-BaWd0puM.js} +0 -0
  335. /package/dist/dashboard/assets/{app-MdVfwraB.js.gz → app-BaWd0puM.js.gz} +0 -0
@@ -366,10 +366,11 @@ export const PROVIDER_CAPABILITIES = {
366
366
  // catalog row behind it.
367
367
  // Dispatch base, not the catalog base: `/zen/v1/models` publishes the
368
368
  // listing while `/inference/v1/models` answers 404, and a byte-exact replay
369
- // against `/inference/v1` returned 200 where `/zen/v1` did not.
369
+ // against `/inference/v1` returned 200 where `/zen/v1` did not. The wire
370
+ // paths are the 2.1 family-segment shapes on the `/inference` origin.
370
371
  endpointPathsByWireFamily: {
371
- chat: "/inference/v1/chat/completions",
372
- responses: "/inference/v1/responses",
372
+ chat: "/inference/openai/v1/chat/completions",
373
+ responses: "/inference/openai/v1/responses",
373
374
  },
374
375
  loadAdapter: async () => createApiKeyAdapter((await import("./integrations/opencode/provider")).OPENCODE_FREE_SPEC),
375
376
  loadModels: async () => (await import("./integrations/opencode/provider")).OPENCODE_FREE_MODELS,
@@ -386,8 +387,8 @@ export const PROVIDER_CAPABILITIES = {
386
387
  },
387
388
  "opencode-zen": {
388
389
  endpointPathsByWireFamily: {
389
- chat: "/inference/v1/chat/completions",
390
- responses: "/inference/v1/responses",
390
+ chat: "/inference/openai/v1/chat/completions",
391
+ responses: "/inference/openai/v1/responses",
391
392
  },
392
393
  loadAdapter: async () => createApiKeyAdapter((await import("./integrations/opencode/provider")).OPENCODE_ZEN_SPEC),
393
394
  loadModels: async () => (await import("./integrations/opencode/provider")).OPENCODE_ZEN_MODELS,
@@ -554,6 +555,21 @@ export const PROVIDER_CAPABILITIES = {
554
555
  });
555
556
  },
556
557
  },
558
+ inception: {
559
+ // Chat only: Mercury 3 / 2.5 / 2 answer the OpenAI-compatible chat wire.
560
+ // The base is version-rooted (`/v1`), so the path is declared below it.
561
+ endpointPathsByWireFamily: { chat: "/chat/completions" },
562
+ loadAdapter: async () => createApiKeyAdapter((await import("./integrations/inception/provider")).INCEPTION_SPEC),
563
+ loadModels: async () => (await import("./integrations/inception/provider")).INCEPTION_MODELS,
564
+ // The listing is bearer-authenticated and the whole provider is pinned to
565
+ // its free allowance, so every fetched row is stamped free-tier.
566
+ loadModelDiscovery: async () => async ({ baseUrl, credential, fetcher }) =>
567
+ (await import("./integrations/inception/provider")).discoverInceptionFreeModels({
568
+ baseUrl,
569
+ ...(typeof credential === "string" && credential.length > 0 ? { credential } : {}),
570
+ ...(fetcher === undefined ? {} : { fetcher }),
571
+ }),
572
+ },
557
573
  openrouter: {
558
574
  loadAdapter: async () => createApiKeyAdapter((await import("./integrations/openrouter/provider")).OPENROUTER_SPEC),
559
575
  // One dynamic import for both halves: this registry lazy-loads every
@@ -168,6 +168,8 @@ export interface ProbeTarget {
168
168
  * so the probe tests the route, not an empty stand-in.
169
169
  */
170
170
  readonly capabilityProfile: Readonly<Record<string, boolean>>;
171
+ /** The model row's output ceiling, when it declares one. */
172
+ readonly outputLimit?: number | null;
171
173
  }
172
174
 
173
175
  /**
@@ -217,10 +219,11 @@ export async function resolveProbeTarget(args: {
217
219
  let serviceKind: ServiceKind = requestedServiceKind;
218
220
  // Capability columns for the row the probe lands on. Resolved alongside the
219
221
  // wire so the profile describes the same row the probe dispatches to.
220
- let capabilitySource: { modalities: unknown; reasoning: boolean; toolCall: boolean } = {
222
+ let capabilitySource: { modalities: unknown; reasoning: boolean; toolCall: boolean; outputLimit: number | null } = {
221
223
  modalities: null,
222
224
  reasoning: false,
223
225
  toolCall: false,
226
+ outputLimit: null,
224
227
  };
225
228
  if (request.wireFamily) {
226
229
  wireFamily = request.wireFamily as WireFamily;
@@ -242,6 +245,7 @@ export async function resolveProbeTarget(args: {
242
245
  modalities: staticDef.modalities,
243
246
  reasoning: staticDef.reasoning,
244
247
  toolCall: staticDef.toolCall,
248
+ outputLimit: staticDef.outputLimit ?? null,
245
249
  };
246
250
  } else {
247
251
  const existing = await db
@@ -252,6 +256,7 @@ export async function resolveProbeTarget(args: {
252
256
  modalities: models.modalities,
253
257
  reasoning: models.reasoning,
254
258
  toolCall: models.toolCall,
259
+ outputLimit: models.outputLimit,
255
260
  })
256
261
  .from(models)
257
262
  .where(
@@ -277,6 +282,7 @@ export async function resolveProbeTarget(args: {
277
282
  modalities: existing[0].modalities,
278
283
  reasoning: existing[0].reasoning,
279
284
  toolCall: existing[0].toolCall,
285
+ outputLimit: existing[0].outputLimit ?? null,
280
286
  };
281
287
  } else {
282
288
  if (requestedServiceKind !== "llm") {
@@ -334,7 +340,7 @@ export async function resolveProbeTarget(args: {
334
340
  toolCall: capabilitySource.toolCall,
335
341
  providerId,
336
342
  });
337
- return { wireFamily, serviceKind, endpointPath, sourceSurface, capabilityProfile };
343
+ return { wireFamily, serviceKind, endpointPath, sourceSurface, capabilityProfile, outputLimit: capabilitySource.outputLimit };
338
344
  }
339
345
 
340
346
  /** The account a probe selected, or the operator-facing reason it could not. */
@@ -752,12 +758,19 @@ export function computeProbeVerdict(args: {
752
758
  : status !== undefined
753
759
  ? `upstream ${status}`
754
760
  : undefined;
761
+ // A 429 that names a free-tier usage limit is upstream quota state, not a
762
+ // payload or egress defect — label it as the throttle it is instead of a
763
+ // generic probe failure.
764
+ const throttled =
765
+ status === 429 ||
766
+ providerStatus === 429 ||
767
+ /free usage limit|rate limit|throttl/i.test(base);
755
768
  // The toast title already names the model. Repeating `provider/model via
756
769
  // wire path` in the body made the operator read the same identity twice
757
770
  // and buried the upstream sentence. The wire family stays, because that is
758
771
  // the one fact the title does not carry; the path is noise next to it.
759
772
  const tagged = statusTag !== undefined ? `${pretty} (${statusTag})` : pretty;
760
- errorMessage = `${wireFamily} — ${tagged}${upstreamReq ? ` [req ${upstreamReq}]` : ""}`;
773
+ errorMessage = `${wireFamily} — ${throttled ? "Upstream rate limit (HTTP 429): " : ""}${tagged}${upstreamReq ? ` [req ${upstreamReq}]` : ""}`;
761
774
 
762
775
  } else if (terminal?.type === "terminal" && terminal.state !== "complete") {
763
776
  // Read the terminal's own detail before falling back to the stop reason.
@@ -192,7 +192,7 @@ export class ProviderProbingService {
192
192
  const providerWireRow = await this.loadProviderWireRow(providerId);
193
193
  const requiresAccount = providerWireRow?.requiresAccount ?? true;
194
194
 
195
- const { wireFamily, serviceKind, endpointPath, sourceSurface, capabilityProfile } = await resolveProbeTarget({
195
+ const { wireFamily, serviceKind, endpointPath, sourceSurface, capabilityProfile, outputLimit } = await resolveProbeTarget({
196
196
  db: this.db,
197
197
  bundledModelCatalog: this.bundledModelCatalog,
198
198
  defaultEndpoints: this.defaultEndpoints,
@@ -253,6 +253,9 @@ export class ProviderProbingService {
253
253
  // over capabilities the route actually has (`prompt_caching`, `tools`,
254
254
  // `reasoning`), reporting a healthy provider as broken.
255
255
  capabilities: capabilityProfile,
256
+ // The model row's own output ceiling, so a probe with a max-output
257
+ // ceiling (OpenCode free) shapes with the model's limit, not a guess.
258
+ ...(outputLimit == null ? {} : { output_limit: outputLimit }),
256
259
  };
257
260
 
258
261
  const probeSignal = AbortSignal.timeout(30_000);
@@ -17,7 +17,7 @@ import type { CanonicalEvent, CanonicalRequest } from "../../protocol/canonical-
17
17
  import { GatewayError } from "../../protocol/gateway-error";
18
18
  import { buildClaudeMessagesRequest, filterClaudeCustomHeaders } from "./shared/claude-messages";
19
19
  import { sendClaudeMessagesRequest } from "../../protocol/upstream/send-claude-messages";
20
- import { applyParamQuirks } from "../../transport/translation/quirks";
20
+ import { applyParamQuirks } from "../../protocol/translation/quirks";
21
21
  import type { ProviderDispatchTarget, ProviderAdapter, ProviderDispatchContext, ProviderId } from "../provider-contracts";
22
22
  import { providerBaseUrl } from "../provider-metadata";
23
23
 
@@ -42,7 +42,6 @@ export const BUDDY_INTL_EFFORTS: Readonly<Record<string, readonly string[]>> = {
42
42
  "glm-5.2": ["high", "xhigh"],
43
43
  "glm-5.3": ["low", "high", "max"],
44
44
  hy3: ["low", "high"],
45
- "hy3-preview": ["low", "high"],
46
45
  "hy4-preview-f": ["high"],
47
46
  };
48
47
  export const BUDDY_CN_EFFORTS: Readonly<Record<string, readonly string[]>> = {
@@ -54,7 +53,6 @@ export const BUDDY_CN_EFFORTS: Readonly<Record<string, readonly string[]>> = {
54
53
  "glm-5.3": ["low", "high", "max"],
55
54
  "glm-5.3-flash": ["low", "high", "max"],
56
55
  hy3: ["low", "high"],
57
- "hy3-preview": ["low", "high"],
58
56
  "hy4-preview": ["high"],
59
57
  };
60
58
 
@@ -336,7 +334,7 @@ export const BUDDY_SHARED_RAW: readonly BuddyRawEntry[] = [
336
334
  ["glm-5.1", "GLM-5.1", true, false, 200_000, 131_072],
337
335
  ["glm-5.2", "GLM-5.2", true, true, 1_000_000, 48_000],
338
336
  ["glm-5.3", "GLM-5.3", true, true, 1_000_000, 48_000],
339
- ["glm-5.3-flash", "GLM-5.3 Flash", true, true, 1_000_000, 32_000],
337
+ ["glm-5.3-flash", "GLM-5.3 Flash", true, false, 1_000_000, 32_000],
340
338
  ["glm-5v-turbo", "GLM-5V Turbo", true, true, 200_000, 131_072],
341
339
  ["gpt-5.3-codex", "GPT-5.3 Codex", true, true, 400_000, 128_000],
342
340
  ["gpt-5.4", "GPT-5.4", true, true, 272_000, 72_000],
@@ -351,7 +349,6 @@ export const BUDDY_SHARED_RAW: readonly BuddyRawEntry[] = [
351
349
  ["grok-4.6", "Grok 4.6", true, true, 500_000, 500_000],
352
350
  ["grok-4.7", "Grok 4.7", true, true, 500_000, 128_000],
353
351
  ["hy3", "Hy3", true, true, 192_000, 64_000, true],
354
- ["hy3-preview", "Hy3 Preview", true, false, 262_144, 64_000],
355
352
  ["hy4-preview", "Hy4 Preview", true, true, 1_000_000, 64_000, true],
356
353
  ["hy4-preview-f", "Hy4 Preview F", true, true, 1_000_000, 64_000, true],
357
354
  ["kimi-k2.5", "Kimi K2.5", true, false, 164_000, 262_144],
@@ -21,14 +21,14 @@ import { createHash } from "node:crypto";
21
21
  import { completeRequiredSchema } from "../../../protocol/codecs";
22
22
  import type { CanonicalRequest } from "../../../protocol/canonical-model";
23
23
  import { isRecord } from "../../../protocol/codecs";
24
- import { applyDeepSeekReasoning, applyOpenAIReasoning, backfillDeepSeekReasoningContent, clampedEffortOutputFloor } from "../../provider-reasoning";
24
+ import { applyDeepSeekReasoning, applyOpenAIReasoning, backfillDeepSeekReasoningContent, clampedEffortOutputFloor } from "../../../protocol/reasoning";
25
25
  import { BUDDY_CN_EFFORTS, BUDDY_INTL_EFFORTS, BUDDY_SHARED_RAW } from "./buddy-catalog-shared";
26
26
  // Imported for the CN roster only: a CN-only id has no row in the shared
27
27
  // table, and without one `raiseOutputForRaisedEffort` silently no-ops, so a
28
28
  // raised effort still answers inside a budget sized for the lower one. It is
29
29
  // read inside the function below — never at module init — so the cycle with
30
30
  // cn-provider (which imports this module) resolves before any call.
31
- import { CODEBUDDY_CN_RAW } from "./codebuddy/cn-provider";
31
+ import { CODEBUDDY_CN_RAW } from "./codebuddy/cn-catalog";
32
32
  import { buddyAccountUid } from "./buddy-oauth-shared";
33
33
  import type { ProviderDispatchContext } from "../../provider-contracts";
34
34
 
@@ -987,13 +987,146 @@ export function ensureBuddyLeadingSystem(
987
987
  * (`11151`), then guarantee a leading system turn (`11128`). The system text
988
988
  * is the variant's own prompt, so each pre-payload hook passes its prompt in.
989
989
  */
990
- function finalizeBuddyMessages(
990
+ /**
991
+ * Longest message text that gets an automatic ephemeral prompt-cache marker,
992
+ * mirroring the reference gateway (`MIN_AUTO_CACHE_TEXT_LENGTH`).
993
+ */
994
+ const MIN_AUTO_CACHE_TEXT_LENGTH = 1024;
995
+
996
+ /**
997
+ * Marks one message's text content cacheable in place: the system turn and the
998
+ * last user turn are the long, stable ones a prefix cache serves best. A
999
+ * string message becomes a typed text block; a text part inside an array gets
1000
+ * `cache_control: {type: "ephemeral"}`. Only the first qualifying text part of
1001
+ * a message is marked, like the reference.
1002
+ */
1003
+ function markCacheableTextContent(message: Record<string, unknown>): void {
1004
+ const content = message["content"];
1005
+ if (typeof content === "string") {
1006
+ if (content.trim().length >= MIN_AUTO_CACHE_TEXT_LENGTH) {
1007
+ message["content"] = [
1008
+ { type: "text", text: content, cache_control: { type: "ephemeral" } },
1009
+ ];
1010
+ }
1011
+ return;
1012
+ }
1013
+ if (!Array.isArray(content)) return;
1014
+ for (let index = 0; index < content.length; index += 1) {
1015
+ const part = content[index];
1016
+ if (!isRecord(part) || part["type"] !== "text") continue;
1017
+ const text = typeof part["text"] === "string" ? part["text"] : "";
1018
+ if (text.trim().length >= MIN_AUTO_CACHE_TEXT_LENGTH && part["cache_control"] === undefined) {
1019
+ content[index] = { ...part, cache_control: { type: "ephemeral" } };
1020
+ return;
1021
+ }
1022
+ }
1023
+ }
1024
+
1025
+ /**
1026
+ * Auto-injects ephemeral prompt-cache markers on the system turn and the last
1027
+ * user turn when the caller declared none. Mirrors `applyPromptCacheControl` in
1028
+ * the reference gateway: an explicit `cache_control` anywhere is respected and
1029
+ * disables the auto-pass so the caller's own caching strategy wins.
1030
+ */
1031
+ export function applyBuddyAutoPromptCache(messages: Array<Record<string, unknown>>): void {
1032
+ const hasExplicit = messages.some((message) =>
1033
+ Array.isArray(message["content"]) &&
1034
+ (message["content"] as Array<Record<string, unknown>>).some(
1035
+ (part) => isRecord(part) && part["cache_control"] !== undefined,
1036
+ ),
1037
+ );
1038
+ if (hasExplicit) return;
1039
+ if (messages.length === 0) return;
1040
+ const first = messages[0];
1041
+ if (isRecord(first) && first["role"] === "system") markCacheableTextContent(first);
1042
+ let lastUser = -1;
1043
+ for (let index = messages.length - 1; index >= 0; index -= 1) {
1044
+ if (isRecord(messages[index]) && messages[index]!["role"] === "user") {
1045
+ lastUser = index;
1046
+ break;
1047
+ }
1048
+ }
1049
+ if (lastUser >= 0) markCacheableTextContent(messages[lastUser]!);
1050
+ }
1051
+
1052
+ /**
1053
+ * Strips the `content` key from assistant tool_calls-only turns.
1054
+ *
1055
+ * Mirrors the reference gateway's OpenAI transcript: an assistant turn that
1056
+ * only asks for tools carries `tool_calls` and no content at all (Go's
1057
+ * `omitzero`-style SDKs omit the key rather than send `null`/`""`). Keeping
1058
+ * an empty string content invites the upstream to fall back to its prose/DSML
1059
+ * channel; omitting the key keeps it on the structured `tool_calls` channel.
1060
+ */
1061
+ export function omitBuddyAssistantToolCallContent(messages: Array<Record<string, unknown>>): void {
1062
+ for (const message of messages) {
1063
+ if (!isRecord(message) || message["role"] !== "assistant") continue;
1064
+ const calls = message["tool_calls"];
1065
+ if (!Array.isArray(calls) || calls.length === 0) continue;
1066
+ const content = message["content"];
1067
+ if (typeof content === "string" && content.trim().length === 0) {
1068
+ delete message["content"];
1069
+ } else if (Array.isArray(content) && content.length === 0) {
1070
+ delete message["content"];
1071
+ }
1072
+ }
1073
+ }
1074
+
1075
+ /**
1076
+ * Forwards the caller's parallel tool-call intent onto the buddy wire.
1077
+ *
1078
+ * The canonical control is `generation_controls.parallel_tool_calls`; the
1079
+ * reference gateway always passes it through so upstream keeps the structured
1080
+ * tool channel, and the same is applied here.
1081
+ */
1082
+ export function injectBuddyParallelToolCalls(
1083
+ payload: Record<string, unknown>,
1084
+ request: CanonicalRequest,
1085
+ ): void {
1086
+ const parallel = request.generation_controls?.parallel_tool_calls;
1087
+ if (parallel !== undefined) payload["parallel_tool_calls"] = parallel;
1088
+ }
1089
+
1090
+ export interface BuddyMessageFinalizeOptions {
1091
+ /** The fixed leading system prompt installed on the wire. */
1092
+ readonly leadPrompt: string;
1093
+ /** Coalesce consecutive user turns (default true). */
1094
+ readonly coalesceConsecutiveUser?: boolean;
1095
+ /** Drop empty-content turns the upstream rejects (default true). */
1096
+ readonly dropEmpty?: boolean;
1097
+ /** Guarantee the wire opens with a system turn (default true). */
1098
+ readonly ensureLeadingSystem?: boolean;
1099
+ /** Omit content on assistant tool_calls-only turns (default true). */
1100
+ readonly omitToolCallContent?: boolean;
1101
+ /** Auto-inject ephemeral prompt-cache markers (default true). */
1102
+ readonly autoPromptCache?: boolean;
1103
+ }
1104
+
1105
+ /**
1106
+ * The single authoritative buddy message-shape pipeline. Every variant runs
1107
+ * the same sequence — coalesce, drop empty turns, guarantee the leading system
1108
+ * turn, omit content on tool_calls-only assistant turns, then auto-cache — so
1109
+ * the wire shape cannot drift between CodeBuddy intl, CN, and WorkBuddy.
1110
+ */
1111
+ export function finalizeBuddyMessages(
991
1112
  messages: Array<Record<string, unknown>>,
992
- systemPrompt: string,
1113
+ options: BuddyMessageFinalizeOptions,
993
1114
  ): void {
994
- coalesceConsecutiveUserMessages(messages);
995
- dropEmptyBuddyMessages(messages);
996
- ensureBuddyLeadingSystem(messages, systemPrompt);
1115
+ if (options.coalesceConsecutiveUser !== false) {
1116
+ coalesceConsecutiveUserMessages(messages);
1117
+ }
1118
+ if (options.dropEmpty !== false) {
1119
+ dropEmptyBuddyMessages(messages);
1120
+ }
1121
+ if (options.ensureLeadingSystem !== false) {
1122
+ ensureBuddyLeadingSystem(messages, options.leadPrompt);
1123
+ }
1124
+ if (options.omitToolCallContent !== false) {
1125
+ omitBuddyAssistantToolCallContent(messages);
1126
+ }
1127
+ if (options.autoPromptCache !== false) {
1128
+ applyBuddyAutoPromptCache(messages);
1129
+ }
997
1130
  }
998
1131
 
999
1132
  /**
@@ -1035,5 +1168,5 @@ export function applyBuddySystemPrompt(
1035
1168
  messages.push({ ...message });
1036
1169
  }
1037
1170
  }
1038
- finalizeBuddyMessages(messages, systemPrompt);
1171
+ finalizeBuddyMessages(messages, { leadPrompt: systemPrompt });
1039
1172
  }
@@ -24,6 +24,7 @@ import { providerBaseUrl } from "../../provider-metadata";
24
24
  import { sleep } from "../../../shared/sleep";
25
25
  import { WORKBUDDY_DOMAIN } from "./workbuddy/shared";
26
26
  import { BUDDY_PROVIDER_IDS } from "../../provider-metadata";
27
+ import { QODER_CHECKIN_PROVIDER_IDS } from "../qoder/qoder-checkin";
27
28
  import {
28
29
  buildWorkBuddyUserAgent,
29
30
  getWorkBuddyClientVersion,
@@ -38,7 +39,10 @@ import { codebuddyDomain, type CodeBuddyVariant } from "./codebuddy/shared";
38
39
  * shared family set so this list cannot drift from the health recorder's or the
39
40
  * request preparer's.
40
41
  */
41
- export const DAILY_CHECKIN_PROVIDER_IDS: readonly string[] = [...BUDDY_PROVIDER_IDS];
42
+ export const DAILY_CHECKIN_PROVIDER_IDS: readonly string[] = [
43
+ ...BUDDY_PROVIDER_IDS,
44
+ ...QODER_CHECKIN_PROVIDER_IDS,
45
+ ];
42
46
 
43
47
  /** Abort budget for one upstream check-in request. */
44
48
  const TIMEOUT_MS = 15_000;
@@ -337,35 +337,6 @@ export async function redeemBuddyStreakTier(
337
337
  // 3. Streak lottery
338
338
  // ---------------------------------------------------------------------------
339
339
 
340
- const LOTTERY_CHANCES_PATH = "/activity/growth/lottery/chances";
341
- const LOTTERY_DRAW_PATH = "/activity/growth/lottery/draw";
342
-
343
- /** Remaining draw chances granted by a redeemed tier. */
344
- export async function readBuddyLotteryChances(
345
- options: BuddyGrowthCall,
346
- ): Promise<number | null> {
347
- const reply = await call(options, "GET", LOTTERY_CHANCES_PATH);
348
- if (!reply.ok || reply.data === null) return null;
349
- const value = Number(reply.data["balance"]);
350
- return Number.isFinite(value) ? value : null;
351
- }
352
-
353
- /**
354
- * Draws once. A fresh `client_token` per draw: reusing one lets the upstream
355
- * treat the draw as a duplicate and swallow it.
356
- */
357
- export async function drawBuddyLottery(
358
- options: BuddyGrowthCall,
359
- ): Promise<BuddyGrowthResult> {
360
- const reply = await call(options, "POST", LOTTERY_DRAW_PATH, {
361
- client_token: clientToken(options, "draw"),
362
- });
363
- if (refused(reply, 400, ["insufficient", "disabled", "no chance", "未开启"])) {
364
- return { state: "not_eligible", error: null, credit: null };
365
- }
366
- return done(reply);
367
- }
368
-
369
340
  // ---------------------------------------------------------------------------
370
341
  // 4. Cat travel
371
342
  // ---------------------------------------------------------------------------
@@ -523,56 +494,3 @@ export async function adoptFirstBuddy(
523
494
  return done(reply);
524
495
  }
525
496
 
526
- // ---------------------------------------------------------------------------
527
- // 6. Trial gift pack + gift/compensation claims
528
- // ---------------------------------------------------------------------------
529
-
530
- const TRIAL_PATH = "/billing/ide/trial";
531
- const GIFT_PATH = "/billing/meter/claim-gift";
532
- const COMPENSATION_PATH = "/billing/meter/claim-compensation";
533
- /** "Already claimed" on the trial pack. */
534
- const TRIAL_ALREADY_CODE = 14051;
535
-
536
- /**
537
- * Claims the one-off trial credit pack.
538
- *
539
- * International only: the reference reports the CN hosts do not serve this
540
- * endpoint at all, and a 404 there is a `skip`, not a failure.
541
- */
542
- export async function claimBuddyTrial(
543
- options: BuddyGrowthCall,
544
- ): Promise<BuddyGrowthResult> {
545
- const reply = await call(options, "POST", TRIAL_PATH, {});
546
- if (coded(reply, TRIAL_ALREADY_CODE) || refused(reply, 400, ["14051"])) {
547
- return { state: "already", error: null, credit: null };
548
- }
549
- // Not served on this host at all — a 404/405 here is a routing fact, not an
550
- // account problem.
551
- if (reply.status === 404 || reply.status === 405) {
552
- return { state: "skipped", error: null, credit: null };
553
- }
554
- return done(reply);
555
- }
556
-
557
- /**
558
- * Claims the newcomer gift, if one is present.
559
- *
560
- * Once per account; a business error afterwards is the normal "already claimed"
561
- * answer and is never a failure.
562
- */
563
- export async function claimBuddyGift(
564
- options: BuddyGrowthCall,
565
- ): Promise<BuddyGrowthResult> {
566
- const reply = await call(options, "POST", GIFT_PATH, {});
567
- if (!reply.ok) return { state: "already", error: null, credit: null };
568
- return done(reply);
569
- }
570
-
571
- /** Claims an activity compensation, if one is present. Same once-only semantics. */
572
- export async function claimBuddyCompensation(
573
- options: BuddyGrowthCall,
574
- ): Promise<BuddyGrowthResult> {
575
- const reply = await call(options, "POST", COMPENSATION_PATH, {});
576
- if (!reply.ok) return { state: "already", error: null, credit: null };
577
- return done(reply);
578
- }
@@ -1,4 +1,5 @@
1
1
  import type { ProviderDispatchContext } from "../../provider-contracts";
2
+ import { createHash } from "node:crypto";
2
3
  import { decodeJwtPayload } from "../../authentication/oauth-flow-store";
3
4
 
4
5
  /**
@@ -96,3 +97,27 @@ export function buddyEnterprise(context?: ProviderDispatchContext): string | und
96
97
  }
97
98
  return undefined;
98
99
  }
100
+
101
+ /**
102
+ * Per-account device binding (`qimei36`) for the buddy providers.
103
+ *
104
+ * The desktop auth state sends `X-Device-Id-List: {"qimei36":"…"}` alongside
105
+ * the Turing token — a second, independent device identity. Pinning it per
106
+ * account makes every account in a pool present a distinct device to the
107
+ * upstream risk control instead of sharing the machine's single fingerprint.
108
+ *
109
+ * Resolution order:
110
+ * 1. `auth_state.qimei36` — explicit operator-set binding (imported or edited).
111
+ * 2. Determinstic derivation from the internal account id (sha256 → 32 hex).
112
+ * This keeps the binding stable across restarts without a database write,
113
+ * and gives each freshly-added account its own id automatically.
114
+ *
115
+ * Returns `undefined` only when no account id is available (unauthenticated).
116
+ */
117
+ export function resolveBuddyDeviceBinding(context?: ProviderDispatchContext): string | undefined {
118
+ const explicit = context?.credential.auth_state?.["qimei36"];
119
+ if (typeof explicit === "string" && explicit.trim().length > 0) return explicit.trim();
120
+ const accountId = context?.credential.account_id;
121
+ if (accountId === undefined || accountId.length === 0) return undefined;
122
+ return createHash("sha256").update(`wb2a:qimei36:${accountId}`).digest("hex").slice(0, 32);
123
+ }