cartethyia 2.7.3 → 2.7.5

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 (625) hide show
  1. package/.env.example +2 -0
  2. package/README.md +17 -5
  3. package/dist/dashboard/assets/{BannedPage-2TJD37Hb.js → BannedPage-Cj15GsMp.js} +1 -1
  4. package/dist/dashboard/assets/BannedPage-Cj15GsMp.js.gz +0 -0
  5. package/dist/dashboard/assets/ClaimGiftsButton-D83pBJ6J.js +1 -0
  6. package/dist/dashboard/assets/ClaimGiftsButton-D83pBJ6J.js.gz +0 -0
  7. package/dist/dashboard/assets/{CliToolDetailPage-xefJNRmJ.js → CliToolDetailPage-DZKmVuJN.js} +1 -1
  8. package/dist/dashboard/assets/CliToolDetailPage-DZKmVuJN.js.gz +0 -0
  9. package/dist/dashboard/assets/{CliToolsPage-Rj9iHQ3F.js → CliToolsPage-BIccZmeL.js} +1 -1
  10. package/dist/dashboard/assets/CliToolsPage-BIccZmeL.js.gz +0 -0
  11. package/dist/dashboard/assets/CombosPage-DEEKkYD-.js +1 -0
  12. package/dist/dashboard/assets/CombosPage-DEEKkYD-.js.gz +0 -0
  13. package/dist/dashboard/assets/ConsoleLogPage-D5rol2j2.js +1 -0
  14. package/dist/dashboard/assets/ConsoleLogPage-D5rol2j2.js.gz +0 -0
  15. package/dist/dashboard/assets/CustomizationPage-BKj_r0rp.js +1 -0
  16. package/dist/dashboard/assets/CustomizationPage-BKj_r0rp.js.gz +0 -0
  17. package/dist/dashboard/assets/InjectSystemPromptPanel-B5nZvmf0.js +656 -0
  18. package/dist/dashboard/assets/InjectSystemPromptPanel-B5nZvmf0.js.gz +0 -0
  19. package/dist/dashboard/assets/{LoginPage-C4w7vea-.js → LoginPage-DkXb6dx-.js} +1 -1
  20. package/dist/dashboard/assets/LoginPage-DkXb6dx-.js.gz +0 -0
  21. package/dist/dashboard/assets/{ModelPicker-BtPcAfot.js → ModelPicker-DnQPSIA8.js} +1 -1
  22. package/dist/dashboard/assets/ModelPicker-DnQPSIA8.js.gz +0 -0
  23. package/dist/dashboard/assets/OverviewPage-Ci2wQKUz.js +1 -0
  24. package/dist/dashboard/assets/OverviewPage-Ci2wQKUz.js.gz +0 -0
  25. package/dist/dashboard/assets/ProviderDetailPage-odlgYrOg.js +5 -0
  26. package/dist/dashboard/assets/ProviderDetailPage-odlgYrOg.js.gz +0 -0
  27. package/dist/dashboard/assets/ProviderIcon-EfeloBn4.js +1 -0
  28. package/dist/dashboard/assets/ProviderIcon-EfeloBn4.js.gz +0 -0
  29. package/dist/dashboard/assets/ProvidersPage-CqFBQEB8.js +1 -0
  30. package/dist/dashboard/assets/ProvidersPage-CqFBQEB8.js.gz +0 -0
  31. package/dist/dashboard/assets/ProxyPage-5SnUSy3j.js +9 -0
  32. package/dist/dashboard/assets/ProxyPage-5SnUSy3j.js.gz +0 -0
  33. package/dist/dashboard/assets/QuotaPage-CmnMDzhP.js +1 -0
  34. package/dist/dashboard/assets/QuotaPage-CmnMDzhP.js.gz +0 -0
  35. package/dist/dashboard/assets/SettingsPage-BJtRZ4qD.js +1 -0
  36. package/dist/dashboard/assets/SettingsPage-BJtRZ4qD.js.gz +0 -0
  37. package/dist/dashboard/assets/{SetupPage-BQS0Rx_u.js → SetupPage-WTa0sktC.js} +1 -1
  38. package/dist/dashboard/assets/SetupPage-WTa0sktC.js.gz +0 -0
  39. package/dist/dashboard/assets/{StudioPage-TP7voAQ0.js → StudioPage-BfAb5-o_.js} +3 -3
  40. package/dist/dashboard/assets/StudioPage-BfAb5-o_.js.gz +0 -0
  41. package/dist/dashboard/assets/UsagePage-BNzeZ2Vk.js +56 -0
  42. package/dist/dashboard/assets/UsagePage-BNzeZ2Vk.js.gz +0 -0
  43. package/dist/dashboard/assets/api-keys-l29JROPv.js +1 -0
  44. package/dist/dashboard/assets/api-keys-l29JROPv.js.gz +0 -0
  45. package/dist/dashboard/assets/app--PJWq8_C.css +2 -0
  46. package/dist/dashboard/assets/app--PJWq8_C.css.gz +0 -0
  47. package/dist/dashboard/assets/{assert-C0YYuZJ4.js → assert-C0K-c4eo.js} +1 -1
  48. package/dist/dashboard/assets/assert-C0K-c4eo.js.gz +0 -0
  49. package/dist/dashboard/assets/{badge-B_DMpMJN.js → badge-DfbhQU4k.js} +1 -1
  50. package/dist/dashboard/assets/badge-DfbhQU4k.js.gz +0 -0
  51. package/dist/dashboard/assets/{banner-DMrMPmth.js → banner-BllT4LcF.js} +1 -1
  52. package/dist/dashboard/assets/banner-BllT4LcF.js.gz +0 -0
  53. package/dist/dashboard/assets/brand/login_register.jpg +0 -0
  54. package/dist/dashboard/assets/brand/login_register_initial.jpg +0 -0
  55. package/dist/dashboard/assets/brand/og_bansos.jpg +0 -0
  56. package/dist/dashboard/assets/brand/og_image.jpg +0 -0
  57. package/dist/dashboard/assets/{card-Be5Uzamx.js → card-CtkX66Wd.js} +1 -1
  58. package/dist/dashboard/assets/card-CtkX66Wd.js.gz +0 -0
  59. package/dist/dashboard/assets/chevron-left-Bf6CaTSJ.js +1 -0
  60. package/dist/dashboard/assets/chevron-left-Bf6CaTSJ.js.gz +0 -0
  61. package/dist/dashboard/assets/{cli-tools-muFRphDu.js → cli-tools-CQXVMn4P.js} +1 -1
  62. package/dist/dashboard/assets/cli-tools-CQXVMn4P.js.gz +0 -0
  63. package/dist/dashboard/assets/{clipboard-button-DOfrVmsi.js → clipboard-button-BQqBQd2n.js} +1 -1
  64. package/dist/dashboard/assets/clipboard-button-BQqBQd2n.js.gz +0 -0
  65. package/dist/dashboard/assets/{dialog-C9omdjQt.js → dialog-BEK7KP0H.js} +1 -1
  66. package/dist/dashboard/assets/dialog-BEK7KP0H.js.gz +0 -0
  67. package/dist/dashboard/assets/{drawer-Bm8WKaGY.js → drawer-BrTEZ8zu.js} +1 -1
  68. package/dist/dashboard/assets/drawer-BrTEZ8zu.js.gz +0 -0
  69. package/dist/dashboard/assets/entry-CHWxbS56.js +1 -0
  70. package/dist/dashboard/assets/entry-CHWxbS56.js.gz +0 -0
  71. package/dist/dashboard/assets/{entry-q5HwZ8nN.js → entry-EBHJ3DrE.js} +1 -1
  72. package/dist/dashboard/assets/entry-EBHJ3DrE.js.gz +0 -0
  73. package/dist/dashboard/assets/entry-LCS85hiq.js +4 -0
  74. package/dist/dashboard/assets/entry-LCS85hiq.js.gz +0 -0
  75. package/dist/dashboard/assets/{formatters-BTScrQpe.js → formatters-BAACA727.js} +1 -1
  76. package/dist/dashboard/assets/formatters-BAACA727.js.gz +0 -0
  77. package/dist/dashboard/assets/{github-badge-Orjuzemr.js → github-badge-YSCE4UC2.js} +1 -1
  78. package/dist/dashboard/assets/github-badge-YSCE4UC2.js.gz +0 -0
  79. package/dist/dashboard/assets/{index-cPrX5vn6.js → index-FQlj0HUT.js} +2 -2
  80. package/dist/dashboard/assets/index-FQlj0HUT.js.gz +0 -0
  81. package/dist/dashboard/assets/{inline-Dvux8w-0.js → inline-C4gU5iOL.js} +1 -1
  82. package/dist/dashboard/assets/inline-C4gU5iOL.js.gz +0 -0
  83. package/dist/dashboard/assets/{input-CU1Rgf3p.js → input-BHHX_Tmm.js} +1 -1
  84. package/dist/dashboard/assets/input-BHHX_Tmm.js.gz +0 -0
  85. package/dist/dashboard/assets/{layout-C_7A6H55.js → layout-CajrYUnD.js} +1 -1
  86. package/dist/dashboard/assets/{layout-C_7A6H55.js.gz → layout-CajrYUnD.js.gz} +0 -0
  87. package/dist/dashboard/assets/{list-row-CzFvogbj.js → list-row-Bx2I7XUe.js} +1 -1
  88. package/dist/dashboard/assets/list-row-Bx2I7XUe.js.gz +0 -0
  89. package/dist/dashboard/assets/live-DNNqRbj2.js +1 -0
  90. package/dist/dashboard/assets/live-DNNqRbj2.js.gz +0 -0
  91. package/dist/dashboard/assets/minimize-2-BTmMTK4o.js +1 -0
  92. package/dist/dashboard/assets/minimize-2-BTmMTK4o.js.gz +0 -0
  93. package/dist/dashboard/assets/{mutation-JJGQm8TD.js → mutation-Rr0TIvcj.js} +1 -1
  94. package/dist/dashboard/assets/mutation-Rr0TIvcj.js.gz +0 -0
  95. package/dist/dashboard/assets/network-C5UBACrJ.js +1 -0
  96. package/dist/dashboard/assets/network-C5UBACrJ.js.gz +0 -0
  97. package/dist/dashboard/assets/numbers-CUWAZK4J.js +2 -0
  98. package/dist/dashboard/assets/numbers-CUWAZK4J.js.gz +0 -0
  99. package/dist/dashboard/assets/pagination-Xsi9Qx04.js +1 -0
  100. package/dist/dashboard/assets/pagination-Xsi9Qx04.js.gz +0 -0
  101. package/dist/dashboard/assets/{query-keys-CLaNz8Xz.js → query-keys-BloKtaXJ.js} +1 -1
  102. package/dist/dashboard/assets/query-keys-BloKtaXJ.js.gz +0 -0
  103. package/dist/dashboard/assets/quota-DvQbD_sr.js +1 -0
  104. package/dist/dashboard/assets/quota-DvQbD_sr.js.gz +0 -0
  105. package/dist/dashboard/assets/{routing-BEH32QOC.js → routing-in0f_IfK.js} +1 -1
  106. package/dist/dashboard/assets/routing-in0f_IfK.js.gz +0 -0
  107. package/dist/dashboard/assets/{select-B3iTetHN.js → select-BLULNqTp.js} +2 -2
  108. package/dist/dashboard/assets/select-BLULNqTp.js.gz +0 -0
  109. package/dist/dashboard/assets/settings-CcGczj6I.js +1 -0
  110. package/dist/dashboard/assets/settings-CcGczj6I.js.gz +0 -0
  111. package/dist/dashboard/assets/shield-BMQNxORh.js +1 -0
  112. package/dist/dashboard/assets/shield-BMQNxORh.js.gz +0 -0
  113. package/dist/dashboard/assets/{stack-KrOrm7JZ.js → stack-D2lIR0tg.js} +1 -1
  114. package/dist/dashboard/assets/stack-D2lIR0tg.js.gz +0 -0
  115. package/dist/dashboard/assets/{state-DLS7IDMy.js → state-DTARjfiW.js} +1 -1
  116. package/dist/dashboard/assets/state-DTARjfiW.js.gz +0 -0
  117. package/dist/dashboard/assets/{switch-42ItvbAs.js → switch-c0oPSnqW.js} +1 -1
  118. package/dist/dashboard/assets/switch-c0oPSnqW.js.gz +0 -0
  119. package/dist/dashboard/assets/{useMutation-Dmnfh1Ue.js → useMutation-m8WGNPrz.js} +1 -1
  120. package/dist/dashboard/assets/useMutation-m8WGNPrz.js.gz +0 -0
  121. package/dist/dashboard/console-backgrounds.jpg +0 -0
  122. package/dist/dashboard/console-backgrounds_initial.jpg +0 -0
  123. package/dist/dashboard/index.html +2 -2
  124. package/dist/dashboard/index.html.gz +0 -0
  125. package/dist/dashboard/precompressed-assets.json +59 -57
  126. package/migrations/full-postgres/0027_qoder_identity_genuine_seed.sql +9 -10
  127. package/migrations/full-postgres/0032_add_provider_pool_accounts.sql +53 -0
  128. package/migrations/full-postgres/0033_add_api_key_ban.sql +9 -0
  129. package/migrations/full-postgres/0034_add_api_key_ban_until.sql +7 -0
  130. package/migrations/full-postgres/0035_align_pool_account_status_enum.sql +21 -0
  131. package/migrations/full-postgres/0036_system_prompt_presets_refs.sql +10 -0
  132. package/migrations/full-postgres/0037_share_concurrency_scope.sql +3 -0
  133. package/migrations/full-postgres/0038_add_accounts_quota_snapshot.sql +7 -0
  134. package/migrations/full-postgres/0039_add_telemetry_provider_model_indexes.sql +13 -0
  135. package/migrations/full-postgres/0040_add_models_dialect.sql +3 -0
  136. package/migrations/full-postgres/0041_add_daily_checkin_ledger_account_fk.sql +7 -0
  137. package/migrations/lite-sqlite3/0027_qoder_identity_genuine_seed.sql +9 -10
  138. package/migrations/lite-sqlite3/0032_add_provider_pool_accounts.sql +44 -0
  139. package/migrations/lite-sqlite3/0033_add_api_key_ban.sql +8 -0
  140. package/migrations/lite-sqlite3/0034_add_api_key_ban_until.sql +5 -0
  141. package/migrations/lite-sqlite3/0035_align_pool_account_status_enum.sql +6 -0
  142. package/migrations/lite-sqlite3/0036_system_prompt_presets_refs.sql +14 -0
  143. package/migrations/lite-sqlite3/0037_share_concurrency_scope.sql +3 -0
  144. package/migrations/lite-sqlite3/0038_add_accounts_quota_snapshot.sql +4 -0
  145. package/migrations/lite-sqlite3/0039_add_telemetry_provider_model_indexes.sql +6 -0
  146. package/migrations/lite-sqlite3/0040_add_models_dialect.sql +3 -0
  147. package/migrations/lite-sqlite3/0041_add_daily_checkin_ledger_account_fk.sql +26 -0
  148. package/package.json +4 -5
  149. package/scripts/cli/update.ts +15 -41
  150. package/src/boot/app.ts +67 -10
  151. package/src/boot/dependencies.ts +786 -732
  152. package/src/boot/lifecycle.ts +11 -12
  153. package/src/config/cooldowns.ts +34 -55
  154. package/src/config/network.ts +8 -4
  155. package/src/config/package-paths.ts +1 -1
  156. package/src/config/providers.ts +13 -14
  157. package/src/config/resources.ts +0 -5
  158. package/src/config/runtime.ts +2 -5
  159. package/src/config/spec.ts +26 -6
  160. package/src/console/api-keys/ban-constants.ts +38 -0
  161. package/src/console/api-keys/contracts.ts +92 -31
  162. package/src/console/api-keys/routes.ts +131 -6
  163. package/src/console/audit/routes.ts +3 -3
  164. package/src/console/auth/access.ts +4 -4
  165. package/src/console/auth/account-reset.ts +5 -5
  166. package/src/console/auth/audit-recorder.ts +1 -1
  167. package/src/console/auth/lockout.ts +7 -3
  168. package/src/console/auth/row-coercion.ts +57 -12
  169. package/src/console/auth/session-resolver.ts +1 -1
  170. package/src/console/auth/session-service.ts +8 -2
  171. package/src/console/auth/session.ts +40 -34
  172. package/src/console/auth/setup-service.ts +4 -3
  173. package/src/console/auth/user-store.ts +1 -1
  174. package/src/console/backup/9router-map.ts +0 -3
  175. package/src/console/backup/contracts.ts +11 -9
  176. package/src/console/backup/export-endpoint.ts +11 -12
  177. package/src/console/backup/nine-router.ts +1 -7
  178. package/src/console/backup/routes.ts +32 -36
  179. package/src/console/backup/service.ts +468 -468
  180. package/src/console/backup/store.ts +47 -29
  181. package/src/console/backup/validate.ts +11 -1
  182. package/src/console/cli-tools/contracts.ts +7 -7
  183. package/src/console/cli-tools/injectors/cline.ts +110 -110
  184. package/src/console/cli-tools/injectors/codex.ts +181 -181
  185. package/src/console/cli-tools/injectors/copilot.ts +108 -108
  186. package/src/console/cli-tools/injectors/driver.ts +247 -247
  187. package/src/console/cli-tools/injectors/jcode.ts +115 -115
  188. package/src/console/cli-tools/injectors/openclaw.ts +161 -161
  189. package/src/console/cli-tools/injectors/opencode.ts +153 -153
  190. package/src/console/cli-tools/routes.ts +4 -4
  191. package/src/console/cli-tools/service.ts +1 -1
  192. package/src/console/cli-tools/store.ts +2 -2
  193. package/src/console/console-router-guards.ts +2 -2
  194. package/src/console/console-router.ts +36 -4
  195. package/src/console/dashboard-assets.ts +9 -9
  196. package/src/console/data-management/routes.ts +3 -3
  197. package/src/console/domain-registration.ts +18 -4
  198. package/src/console/inferhub/client.ts +0 -7
  199. package/src/console/inferhub/key-store.ts +1 -1
  200. package/src/console/inferhub/normalize.ts +2 -2
  201. package/src/console/inferhub/routes.ts +1 -1
  202. package/src/console/inferhub/types.ts +1 -1
  203. package/src/console/observability/contracts.ts +5 -1
  204. package/src/console/observability/live.ts +34 -21
  205. package/src/console/observability/logs.ts +4 -4
  206. package/src/console/observability/performance.ts +3 -3
  207. package/src/console/observability/routes.ts +3 -3
  208. package/src/console/observability/sse.ts +62 -8
  209. package/src/console/observability/store.ts +36 -20
  210. package/src/console/providers/catalog/catalog-projections.ts +1 -1
  211. package/src/console/providers/catalog/contracts.ts +82 -22
  212. package/src/console/providers/catalog/model-operations.ts +1 -1
  213. package/src/console/providers/catalog/provider-operations.ts +110 -17
  214. package/src/console/providers/catalog/public-model-store.ts +0 -0
  215. package/src/console/providers/catalog/routes.ts +30 -6
  216. package/src/console/providers/catalog/store-helpers.ts +349 -326
  217. package/src/console/providers/catalog/store.ts +1662 -1292
  218. package/src/console/providers/detail/routes.ts +4 -4
  219. package/src/console/providers/oauth/callback-listener.ts +100 -18
  220. package/src/console/providers/oauth/routes.ts +95 -16
  221. package/src/console/quota/account-quota-global.ts +41 -3
  222. package/src/console/quota/account-quota-shared.ts +10 -3
  223. package/src/console/quota/account-quota-tenant.ts +285 -67
  224. package/src/console/quota/account-test-egress.ts +4 -4
  225. package/src/console/quota/daily-growth-job.ts +308 -0
  226. package/src/console/quota/native-identity.ts +2 -2
  227. package/src/console/quota/quota-view.ts +1 -1
  228. package/src/console/remote-support/routes.ts +1 -1
  229. package/src/console/remote-support/service.ts +2 -2
  230. package/src/console/routing/model/contracts.ts +1 -1
  231. package/src/console/routing/model/routes.ts +3 -3
  232. package/src/console/routing/pools/contracts.ts +13 -10
  233. package/src/console/routing/pools/import-parse.ts +3 -3
  234. package/src/console/routing/pools/operations.ts +14 -7
  235. package/src/console/routing/pools/probe-verdict.ts +8 -3
  236. package/src/console/routing/pools/relay-deploy.ts +12 -12
  237. package/src/console/routing/pools/routes.ts +3 -3
  238. package/src/console/routing/pools/store.ts +21 -1
  239. package/src/console/settings/contracts.ts +29 -2
  240. package/src/console/settings/routes.ts +3 -3
  241. package/src/console/settings/store.ts +20 -0
  242. package/src/console/share/share-router.ts +112 -44
  243. package/src/console/share/share-stats.ts +109 -39
  244. package/src/console/share/share-usage.ts +1 -1
  245. package/src/console/shared/errors.ts +2 -2
  246. package/src/console/shared/object.ts +18 -0
  247. package/src/console/shared/read-through-cache.ts +42 -6
  248. package/src/console/shared/transaction.ts +14 -0
  249. package/src/console/system-prompt/contracts.ts +89 -12
  250. package/src/console/system-prompt/routes.ts +98 -45
  251. package/src/console/updates/apply-update.ts +244 -0
  252. package/src/console/updates/routes.ts +256 -98
  253. package/src/main.ts +11 -5
  254. package/src/network/http2-bidi.ts +1 -1
  255. package/src/network/ip-utils.ts +298 -0
  256. package/src/network/outbound-fetch.ts +76 -5
  257. package/src/network/pool/agent.ts +7 -6
  258. package/src/network/pool/byte-accounting.ts +1 -1
  259. package/src/network/pool/resolver.ts +55 -51
  260. package/src/network/pool/selector.ts +150 -136
  261. package/src/network/pool-cooldown-store.ts +71 -16
  262. package/src/network/pool-health-machine.ts +22 -2
  263. package/src/network/rate-limit-verdict.ts +68 -0
  264. package/src/network/retry.ts +1 -1
  265. package/src/network/ssrf.ts +24 -75
  266. package/src/observability/event-log.ts +2 -2
  267. package/src/observability/event-query.ts +286 -214
  268. package/src/observability/log-sanitize.ts +1 -1
  269. package/src/observability/metrics.ts +4 -4
  270. package/src/observability/payload-capture.ts +6 -5
  271. package/src/observability/payload-store.ts +1 -1
  272. package/src/observability/runtime-metrics.ts +1 -1
  273. package/src/observability/telemetry-buffer.ts +12 -3
  274. package/src/observability/telemetry-reconciliation.ts +3 -3
  275. package/src/observability/telemetry-status.ts +1 -1
  276. package/src/observability/token-speed.ts +1 -1
  277. package/src/persistence/connection-url.ts +1 -1
  278. package/src/persistence/db-handle.ts +1 -1
  279. package/src/persistence/db-pg.ts +2 -2
  280. package/src/persistence/db-sqlite.ts +10 -5
  281. package/src/persistence/dialect.ts +5 -1
  282. package/src/persistence/readiness.ts +19 -7
  283. package/src/persistence/redis.ts +1 -1
  284. package/src/persistence/schema.ts +177 -45
  285. package/src/persistence/shared-cache.ts +2 -2
  286. package/src/persistence/sort-order.ts +1 -1
  287. package/src/persistence/stores/api-key-store.ts +109 -4
  288. package/src/persistence/stores/share-store.ts +20 -8
  289. package/src/persistence/stores/telemetry-store.ts +1 -1
  290. package/src/persistence/system-prompt-store.ts +63 -18
  291. package/src/persistence/tenant-preferences.ts +1 -1
  292. package/src/persistence/ttl-cache.ts +3 -3
  293. package/src/persistence/usage-flush.ts +7 -2
  294. package/src/protocol/canonical-model.ts +1 -1
  295. package/src/protocol/codecs.ts +12 -16
  296. package/src/protocol/dsml.ts +3 -49
  297. package/src/protocol/gateway-error.ts +6 -3
  298. package/src/protocol/image-clamp.ts +2 -2
  299. package/src/protocol/image-generation.ts +2 -2
  300. package/src/protocol/reasoning-text.ts +1 -1
  301. package/src/protocol/reasoning.ts +7 -52
  302. package/src/protocol/registry.ts +50 -15
  303. package/src/protocol/request/chat.ts +46 -19
  304. package/src/protocol/request/codex.ts +1 -1
  305. package/src/protocol/request/messages.ts +46 -14
  306. package/src/protocol/request/responses.ts +19 -10
  307. package/src/protocol/response/chat.ts +27 -12
  308. package/src/protocol/response/codex.ts +2 -2
  309. package/src/protocol/response/gemini.ts +1 -1
  310. package/src/protocol/response/messages.ts +6 -6
  311. package/src/protocol/response/omp-tool-call-markup.ts +1 -1
  312. package/src/protocol/response/responses.ts +3 -3
  313. package/src/protocol/sse-decoder.ts +64 -4
  314. package/src/protocol/stop-reason.ts +2 -2
  315. package/src/protocol/think-split.ts +347 -0
  316. package/src/protocol/translation/attachment-integrity.ts +3 -3
  317. package/src/protocol/translation/cache-controls.ts +8 -20
  318. package/src/protocol/translation/capabilities.ts +8 -8
  319. package/src/protocol/translation/dialect/deepseek.ts +10 -0
  320. package/src/protocol/translation/dialect/inband.ts +39 -0
  321. package/src/protocol/translation/dialect/model-thinking.ts +89 -0
  322. package/src/protocol/translation/dialect/qwen.ts +114 -0
  323. package/src/protocol/translation/dialect/resolver.ts +83 -0
  324. package/src/protocol/translation/dialect/rules.ts +93 -0
  325. package/src/protocol/translation/dialect/thinking-ladder.ts +245 -0
  326. package/src/protocol/translation/dialect/types.ts +58 -0
  327. package/src/protocol/translation/quirks.ts +18 -4
  328. package/src/protocol/translation/thinking.ts +12 -252
  329. package/src/protocol/translation/tool-id.ts +3 -3
  330. package/src/protocol/translation/tool-repair.ts +1 -1
  331. package/src/protocol/upstream/post-json.ts +6 -1
  332. package/src/protocol/upstream/send-claude-messages.ts +3 -3
  333. package/src/providers/adapters/base-adapter.ts +1 -1
  334. package/src/providers/adapters/compatible-adapter.ts +44 -5
  335. package/src/providers/authentication/jwt-validator.ts +0 -5
  336. package/src/providers/authentication/oauth-client.ts +254 -253
  337. package/src/providers/authentication/oauth-flow-store.ts +42 -7
  338. package/src/providers/authentication/oauth-refresh-service.ts +2 -2
  339. package/src/providers/bundled-discovery-providers.ts +6 -30
  340. package/src/providers/configured-openai-providers.ts +82 -97
  341. package/src/providers/configured-provider.ts +1 -1
  342. package/src/providers/default-registry.ts +23 -35
  343. package/src/providers/discovery/catalog/catalog-format.ts +13 -2
  344. package/src/providers/discovery/discovery-types.ts +0 -1
  345. package/src/providers/discovery/openai-model-discovery.ts +2 -2
  346. package/src/providers/discovery/probe-phases.ts +50 -15
  347. package/src/providers/discovery/probing-service.ts +18 -20
  348. package/src/providers/integrations/agentrouter/agentrouter.ts +14 -5
  349. package/src/providers/integrations/alibabacp/alibabacp.ts +2 -2
  350. package/src/providers/integrations/anthropic.ts +4 -11
  351. package/src/providers/integrations/antigravity/antigravity-oauth.ts +0 -1
  352. package/src/providers/integrations/antigravity/antigravity-protocol.ts +0 -14
  353. package/src/providers/integrations/antigravity/antigravity.ts +15 -26
  354. package/src/providers/integrations/buddy/buddy-catalog-shared.ts +2 -2
  355. package/src/providers/integrations/buddy/buddy-chat-shared.ts +20 -4
  356. package/src/providers/integrations/buddy/buddy-cli-headers.ts +19 -52
  357. package/src/providers/integrations/buddy/buddy-discovery-shared.ts +37 -6
  358. package/src/providers/integrations/buddy/buddy-identity.ts +108 -1
  359. package/src/providers/integrations/buddy/buddy-media.ts +16 -16
  360. package/src/providers/integrations/buddy/buddy-oauth-shared.ts +1 -1
  361. package/src/providers/integrations/buddy/codebuddy/cn-provider.ts +1 -2
  362. package/src/providers/integrations/buddy/codebuddy/provider.ts +5 -16
  363. package/src/providers/integrations/buddy/codebuddy/shared.ts +30 -9
  364. package/src/providers/integrations/buddy/workbuddy/provider.ts +6 -10
  365. package/src/providers/integrations/buddy/workbuddy/shared.ts +21 -3
  366. package/src/providers/integrations/claude/claude-betas.ts +1 -1
  367. package/src/providers/integrations/claude/claude-cch.ts +148 -148
  368. package/src/providers/integrations/claude/claude-credentials.ts +1 -1
  369. package/src/providers/integrations/claude/claude-slow-lane-retry.ts +1 -1
  370. package/src/providers/integrations/claude/claude.ts +3 -10
  371. package/src/providers/integrations/claude/fallback-credit.ts +3 -7
  372. package/src/providers/integrations/cline/cline.ts +2 -2
  373. package/src/providers/integrations/cloudflareai/cloudflareai.ts +4 -7
  374. package/src/providers/integrations/codex/codex-device-code.ts +101 -101
  375. package/src/providers/integrations/codex/codex-errors.ts +1 -1
  376. package/src/providers/integrations/codex/codex-oauth.ts +0 -1
  377. package/src/providers/integrations/codex/codex.ts +14 -15
  378. package/src/providers/integrations/commandcode/commandcode.ts +76 -116
  379. package/src/providers/integrations/commandcode/quota.ts +132 -48
  380. package/src/providers/integrations/cursor/cursor-http2.ts +11 -14
  381. package/src/providers/integrations/cursor/cursor-quota.ts +2 -6
  382. package/src/providers/integrations/cursor/cursor-request.ts +0 -87
  383. package/src/providers/integrations/cursor/cursor-shared.ts +0 -4
  384. package/src/providers/integrations/cursor/cursor.ts +53 -20
  385. package/src/providers/integrations/cursor/protocol/framing.ts +0 -56
  386. package/src/providers/integrations/cursor/protocol/messages.ts +0 -96
  387. package/src/providers/integrations/factory/factory.ts +13 -13
  388. package/src/providers/integrations/gemini.ts +11 -11
  389. package/src/providers/integrations/github/github-discovery.ts +55 -10
  390. package/src/providers/integrations/github/github-oauth.ts +6 -5
  391. package/src/providers/integrations/github/github.ts +47 -20
  392. package/src/providers/integrations/grok/grok-oauth.ts +261 -261
  393. package/src/providers/integrations/grok/grok-quota-frame.ts +1 -1
  394. package/src/providers/integrations/grok/grok-quota.ts +16 -7
  395. package/src/providers/integrations/grok/grok-ratelimit-store.ts +0 -5
  396. package/src/providers/integrations/grok/grok-ratelimit.ts +3 -0
  397. package/src/providers/integrations/grok/grok.ts +88 -42
  398. package/src/providers/integrations/inferhub.ts +4 -11
  399. package/src/providers/integrations/kilo/kilo-oauth.ts +1 -1
  400. package/src/providers/integrations/kimi/kimi.ts +4 -11
  401. package/src/providers/integrations/kiro/kiro-cache.ts +31 -0
  402. package/src/providers/integrations/kiro/kiro-catalog.ts +31 -5
  403. package/src/providers/integrations/kiro/kiro-discovery.ts +47 -43
  404. package/src/providers/integrations/kiro/kiro-machine-id.ts +6 -3
  405. package/src/providers/integrations/kiro/kiro-oauth.ts +523 -255
  406. package/src/providers/integrations/kiro/kiro-quota.ts +35 -47
  407. package/src/providers/integrations/kiro/kiro-request.ts +8 -11
  408. package/src/providers/integrations/kiro/kiro-stream.ts +13 -0
  409. package/src/providers/integrations/kiro/kiro.ts +91 -78
  410. package/src/providers/integrations/muse/muse-oauth.ts +0 -1
  411. package/src/providers/integrations/muse/muse-quota.ts +0 -4
  412. package/src/providers/integrations/muse/muse.ts +6 -9
  413. package/src/providers/integrations/opencode/fingerprint.ts +7 -5
  414. package/src/providers/integrations/opencode/provider.ts +17 -24
  415. package/src/providers/integrations/perplexity.ts +2 -2
  416. package/src/providers/integrations/qoder/qoder-catalog.ts +132 -17
  417. package/src/providers/integrations/qoder/qoder-checkin.ts +26 -8
  418. package/src/providers/integrations/qoder/qoder-native-identity-store.ts +9 -8
  419. package/src/providers/integrations/qoder/qoder-oauth.ts +165 -25
  420. package/src/providers/integrations/qoder/qoder-quota.ts +48 -6
  421. package/src/providers/integrations/qoder/qoder-realm.ts +22 -28
  422. package/src/providers/integrations/qoder/qoder-sse.ts +159 -0
  423. package/src/providers/integrations/qoder/qoder-umid.ts +33 -38
  424. package/src/providers/integrations/qoder/qoder-virtual.ts +14 -2
  425. package/src/providers/integrations/qoder/qoder.ts +521 -63
  426. package/src/providers/integrations/shared/connect.ts +0 -54
  427. package/src/providers/integrations/shared/hash.ts +15 -0
  428. package/src/providers/integrations/shared/session-cache.ts +136 -0
  429. package/src/providers/integrations/tokenharbor.ts +2 -2
  430. package/src/providers/integrations/trae/trae-cache.ts +7 -87
  431. package/src/providers/integrations/trae/trae-catalog.ts +1 -6
  432. package/src/providers/integrations/trae/trae-protocol.ts +17 -8
  433. package/src/providers/integrations/trae/trae-realm.ts +0 -7
  434. package/src/providers/integrations/trae/trae.ts +59 -19
  435. package/src/providers/integrations/xai/xai-oauth.ts +5 -5
  436. package/src/providers/integrations/zcode/oauth.ts +1 -1
  437. package/src/providers/integrations/zcode-start-plan/gifts.ts +265 -0
  438. package/src/providers/integrations/zcode-start-plan/oauth.ts +154 -0
  439. package/src/providers/integrations/zcode-start-plan/provider.ts +131 -0
  440. package/src/providers/integrations/zcode-start-plan/quota.ts +152 -0
  441. package/src/providers/integrations/zcode-start-plan/zcode_prompt.json +10 -0
  442. package/src/providers/operations/account-error-classification.ts +131 -116
  443. package/src/providers/operations/account-health-service.ts +31 -39
  444. package/src/providers/operations/account-reset-service.ts +58 -1
  445. package/src/providers/operations/cli-platform.ts +31 -5
  446. package/src/providers/operations/client-version-resolver.ts +4 -6
  447. package/src/providers/operations/client-versions.ts +10 -7
  448. package/src/providers/operations/provider-catalog-seeder.ts +2 -0
  449. package/src/providers/operations/provider-catalog-service.ts +22 -6
  450. package/src/providers/operations/provider-credential-service.ts +35 -6
  451. package/src/providers/operations/upstream-deadline.ts +3 -3
  452. package/src/providers/provider-contracts.ts +65 -10
  453. package/src/providers/provider-credential-resolver.ts +1 -1
  454. package/src/providers/provider-metadata.ts +17 -2
  455. package/src/providers/provider-model-definition.ts +22 -1
  456. package/src/providers/provider-probe.ts +4 -2
  457. package/src/providers/provider-registry.ts +4 -4
  458. package/src/providers/provider-usage.ts +81 -16
  459. package/src/providers/quota/cache.ts +29 -0
  460. package/src/providers/quota/quota-contracts.ts +28 -10
  461. package/src/providers/quota/refresh.ts +29 -6
  462. package/src/providers/quota/snapshot.ts +94 -0
  463. package/src/providers/search/search-provider.ts +6 -8
  464. package/src/providers/search/search-providers.ts +2 -1
  465. package/src/security/abuse.ts +8 -8
  466. package/src/security/admission/reasons.ts +17 -17
  467. package/src/security/admission/service.ts +2 -2
  468. package/src/security/admission/ttl.ts +61 -61
  469. package/src/security/api-key-auth.ts +20 -8
  470. package/src/security/client-router-fingerprint.ts +4 -4
  471. package/src/security/credential-codec.ts +3 -3
  472. package/src/security/csrf.ts +2 -2
  473. package/src/security/ip-boundary.ts +16 -189
  474. package/src/security/model-access-rule.ts +1 -1
  475. package/src/security/outbound-headers.ts +18 -0
  476. package/src/shared/coerce.ts +19 -0
  477. package/src/shared/invoke-guard.ts +47 -0
  478. package/src/transport/contracts.ts +13 -4
  479. package/src/transport/dispatch/attempt-finalize.ts +5 -4
  480. package/src/transport/dispatch/attempt-loop.ts +10 -7
  481. package/src/transport/dispatch/capture-policy.ts +34 -37
  482. package/src/transport/dispatch/empty-stream.ts +28 -25
  483. package/src/transport/dispatch/generation.ts +33 -53
  484. package/src/transport/dispatch/leases.ts +8 -8
  485. package/src/transport/dispatch/native-shared.ts +127 -0
  486. package/src/transport/dispatch/proxy-request.ts +10 -8
  487. package/src/transport/dispatch/responses-compact.ts +35 -56
  488. package/src/transport/dispatch/retry-policy.ts +4 -67
  489. package/src/transport/dispatch/stream-bridge.ts +1 -1
  490. package/src/transport/dispatch/streaming/encoder.ts +18 -72
  491. package/src/transport/dispatch/streaming/finalize.ts +11 -10
  492. package/src/transport/dispatch/streaming/stream.ts +7 -3
  493. package/src/transport/dispatch/streaming/thinking-loop.ts +1 -1
  494. package/src/transport/dispatch/systemone.ts +27 -55
  495. package/src/transport/dispatch/token-count.ts +49 -46
  496. package/src/transport/dispatch/websearch.ts +24 -50
  497. package/src/transport/drain-endpoint.ts +3 -3
  498. package/src/transport/failure-policy.ts +19 -6
  499. package/src/transport/guards/auth-guard.ts +6 -11
  500. package/src/transport/guards/body-policy.ts +7 -7
  501. package/src/transport/guards/error-lifecycle.ts +18 -3
  502. package/src/transport/guards/guard-errors.ts +7 -3
  503. package/src/transport/guards/headroom-guard.ts +2 -2
  504. package/src/transport/guards/pipeline.ts +7 -7
  505. package/src/transport/guards/readiness-guard.ts +16 -2
  506. package/src/transport/guards/request-context.ts +15 -20
  507. package/src/transport/request/preparer.ts +306 -27
  508. package/src/transport/request/rtk/compress-request.ts +3 -3
  509. package/src/transport/request/rtk/rtk-constants.ts +2 -2
  510. package/src/transport/request/rtk/rtk-filters.ts +8 -8
  511. package/src/transport/request/state.ts +5 -20
  512. package/src/transport/request/system-prompt-injection.ts +74 -11
  513. package/src/transport/request/tenant-preferences.ts +22 -0
  514. package/src/transport/routing/candidate-index.ts +2 -2
  515. package/src/transport/routing/round-robin.ts +1 -1
  516. package/src/transport/routing/route-catalog.ts +113 -19
  517. package/src/transport/routing/route-model.ts +131 -38
  518. package/src/transport/routing/router.ts +154 -39
  519. package/src/transport/shutdown-notice.ts +21 -9
  520. package/src/transport/surface/adapters.ts +3 -3
  521. package/src/transport/surface/chat/encode.ts +7 -7
  522. package/src/transport/surface/chat/parse.ts +7 -7
  523. package/src/transport/surface/completion.ts +3 -3
  524. package/src/transport/surface/content-parts.ts +1 -1
  525. package/src/transport/surface/dialects.ts +2 -2
  526. package/src/transport/surface/messages/encode.ts +1 -1
  527. package/src/transport/surface/messages/parse.ts +3 -3
  528. package/src/transport/surface/responses/adapter.ts +1 -1
  529. package/src/transport/surface/responses/errors.ts +0 -9
  530. package/src/transport/vision-handoff-cache.ts +58 -0
  531. package/src/transport/vision-handoff.ts +376 -0
  532. package/src/workers/account-identity-sweep.ts +2 -2
  533. package/src/workers/buddy-growth-tasks.ts +392 -346
  534. package/src/workers/daily-checkin.ts +102 -49
  535. package/src/workers/maintenance-lease.ts +2 -35
  536. package/src/workers/oauth-refresh-sweep.ts +8 -1
  537. package/src/workers/quota-refresh-sweep.ts +120 -82
  538. package/src/workers/scheduler.ts +67 -4
  539. package/src/workers/sweep-runner.ts +24 -5
  540. package/src/workers/zcode-gift-claim.ts +108 -0
  541. package/dist/dashboard/assets/AccountCooldown-Bjo92Qat.js +0 -1
  542. package/dist/dashboard/assets/AccountCooldown-Bjo92Qat.js.gz +0 -0
  543. package/dist/dashboard/assets/BannedPage-2TJD37Hb.js.gz +0 -0
  544. package/dist/dashboard/assets/CliToolDetailPage-xefJNRmJ.js.gz +0 -0
  545. package/dist/dashboard/assets/CliToolsPage-Rj9iHQ3F.js.gz +0 -0
  546. package/dist/dashboard/assets/CombosPage-JEbYJFxw.js +0 -1
  547. package/dist/dashboard/assets/CombosPage-JEbYJFxw.js.gz +0 -0
  548. package/dist/dashboard/assets/ConsoleLogPage-C3xx_inM.js +0 -1
  549. package/dist/dashboard/assets/ConsoleLogPage-C3xx_inM.js.gz +0 -0
  550. package/dist/dashboard/assets/CustomizationPage-Dw306gJU.js +0 -1
  551. package/dist/dashboard/assets/CustomizationPage-Dw306gJU.js.gz +0 -0
  552. package/dist/dashboard/assets/HealthEventsModal-jaBo1TWV.js +0 -1
  553. package/dist/dashboard/assets/HealthEventsModal-jaBo1TWV.js.gz +0 -0
  554. package/dist/dashboard/assets/LoginPage-C4w7vea-.js.gz +0 -0
  555. package/dist/dashboard/assets/ModelPicker-BtPcAfot.js.gz +0 -0
  556. package/dist/dashboard/assets/OverviewPage-CGC5gNs1.js +0 -1
  557. package/dist/dashboard/assets/OverviewPage-CGC5gNs1.js.gz +0 -0
  558. package/dist/dashboard/assets/ProviderDetailPage-DQcthNGt.js +0 -4
  559. package/dist/dashboard/assets/ProviderDetailPage-DQcthNGt.js.gz +0 -0
  560. package/dist/dashboard/assets/ProviderIcon-FB6yS4v1.js +0 -1
  561. package/dist/dashboard/assets/ProviderIcon-FB6yS4v1.js.gz +0 -0
  562. package/dist/dashboard/assets/ProvidersPage-0OsXRGXe.js +0 -1
  563. package/dist/dashboard/assets/ProvidersPage-0OsXRGXe.js.gz +0 -0
  564. package/dist/dashboard/assets/ProxyPage-DdPc8slb.js +0 -9
  565. package/dist/dashboard/assets/ProxyPage-DdPc8slb.js.gz +0 -0
  566. package/dist/dashboard/assets/QuotaPage-Ci5O4BnH.js +0 -1
  567. package/dist/dashboard/assets/QuotaPage-Ci5O4BnH.js.gz +0 -0
  568. package/dist/dashboard/assets/SettingsPage-CDZjTt87.js +0 -1
  569. package/dist/dashboard/assets/SettingsPage-CDZjTt87.js.gz +0 -0
  570. package/dist/dashboard/assets/SetupPage-BQS0Rx_u.js.gz +0 -0
  571. package/dist/dashboard/assets/SortableList-BAL5npBC.js +0 -1
  572. package/dist/dashboard/assets/SortableList-BAL5npBC.js.gz +0 -0
  573. package/dist/dashboard/assets/StudioPage-TP7voAQ0.js.gz +0 -0
  574. package/dist/dashboard/assets/UsagePage-Cj_fEzhg.js +0 -56
  575. package/dist/dashboard/assets/UsagePage-Cj_fEzhg.js.gz +0 -0
  576. package/dist/dashboard/assets/api-keys-MbI7e7rt.js +0 -1
  577. package/dist/dashboard/assets/api-keys-MbI7e7rt.js.gz +0 -0
  578. package/dist/dashboard/assets/app-B2vyPoII.css +0 -2
  579. package/dist/dashboard/assets/app-B2vyPoII.css.gz +0 -0
  580. package/dist/dashboard/assets/assert-C0YYuZJ4.js.gz +0 -0
  581. package/dist/dashboard/assets/badge-B_DMpMJN.js.gz +0 -0
  582. package/dist/dashboard/assets/banner-DMrMPmth.js.gz +0 -0
  583. package/dist/dashboard/assets/card-Be5Uzamx.js.gz +0 -0
  584. package/dist/dashboard/assets/cli-tools-muFRphDu.js.gz +0 -0
  585. package/dist/dashboard/assets/clipboard-button-DOfrVmsi.js.gz +0 -0
  586. package/dist/dashboard/assets/dialog-C9omdjQt.js.gz +0 -0
  587. package/dist/dashboard/assets/drawer-Bm8WKaGY.js.gz +0 -0
  588. package/dist/dashboard/assets/entry-DS90gUks.js +0 -1
  589. package/dist/dashboard/assets/entry-DS90gUks.js.gz +0 -0
  590. package/dist/dashboard/assets/entry-j40LJotO.js +0 -4
  591. package/dist/dashboard/assets/entry-j40LJotO.js.gz +0 -0
  592. package/dist/dashboard/assets/entry-q5HwZ8nN.js.gz +0 -0
  593. package/dist/dashboard/assets/formatters-BTScrQpe.js.gz +0 -0
  594. package/dist/dashboard/assets/github-badge-Orjuzemr.js.gz +0 -0
  595. package/dist/dashboard/assets/index-cPrX5vn6.js.gz +0 -0
  596. package/dist/dashboard/assets/inline-Dvux8w-0.js.gz +0 -0
  597. package/dist/dashboard/assets/input-CU1Rgf3p.js.gz +0 -0
  598. package/dist/dashboard/assets/list-row-CzFvogbj.js.gz +0 -0
  599. package/dist/dashboard/assets/live-KaG15HLQ.js +0 -1
  600. package/dist/dashboard/assets/live-KaG15HLQ.js.gz +0 -0
  601. package/dist/dashboard/assets/minimize-2-D-MN8W8a.js +0 -1
  602. package/dist/dashboard/assets/minimize-2-D-MN8W8a.js.gz +0 -0
  603. package/dist/dashboard/assets/mutation-JJGQm8TD.js.gz +0 -0
  604. package/dist/dashboard/assets/numbers-BNDZnsUE.js +0 -2
  605. package/dist/dashboard/assets/numbers-BNDZnsUE.js.gz +0 -0
  606. package/dist/dashboard/assets/query-keys-CLaNz8Xz.js.gz +0 -0
  607. package/dist/dashboard/assets/quota-DZGyp0N5.js +0 -1
  608. package/dist/dashboard/assets/quota-DZGyp0N5.js.gz +0 -0
  609. package/dist/dashboard/assets/routing-BEH32QOC.js.gz +0 -0
  610. package/dist/dashboard/assets/select-B3iTetHN.js.gz +0 -0
  611. package/dist/dashboard/assets/settings-Dt3Eqbnq.js +0 -1
  612. package/dist/dashboard/assets/settings-Dt3Eqbnq.js.gz +0 -0
  613. package/dist/dashboard/assets/stack-KrOrm7JZ.js.gz +0 -0
  614. package/dist/dashboard/assets/state-DLS7IDMy.js.gz +0 -0
  615. package/dist/dashboard/assets/stream-retry-5QnVuSSo.js +0 -1
  616. package/dist/dashboard/assets/stream-retry-5QnVuSSo.js.gz +0 -0
  617. package/dist/dashboard/assets/switch-42ItvbAs.js.gz +0 -0
  618. package/dist/dashboard/assets/useMutation-Dmnfh1Ue.js.gz +0 -0
  619. package/src/network/response-headers.ts +0 -39
  620. package/src/protocol/response/dsml.ts +0 -38
  621. package/src/providers/integrations/xiaomi-mimo/think-stream.ts +0 -229
  622. /package/dist/dashboard/assets/{app-wvwCi4Eh.js → app-Bgd3zANQ.js} +0 -0
  623. /package/dist/dashboard/assets/{app-wvwCi4Eh.js.gz → app-Bgd3zANQ.js.gz} +0 -0
  624. /package/dist/dashboard/assets/brand/{og_bansos.webp → og_bansos_initial.webp} +0 -0
  625. /package/dist/dashboard/assets/brand/{og_image.webp → og_image_initial.webp} +0 -0
@@ -22,6 +22,7 @@ import type { CanonicalRequest } from "../../../protocol/canonical-model";
22
22
  import { GatewayError } from "../../../protocol/gateway-error";
23
23
  import { decodeMuseCredential } from "../../../security/credential-codec";
24
24
  import { OpenAICompatibleAdapter, withBearerAuthentication } from "../../adapters/compatible-adapter";
25
+ import { assertCredentialKind } from "../../provider-contracts";
25
26
  import type { ProviderDispatchTarget, ProviderDispatchContext, ResolvedCredential } from "../../provider-contracts";
26
27
  import { providerBaseUrl } from "../../provider-metadata";
27
28
 
@@ -85,19 +86,19 @@ export function parseMuseCodeCredential(
85
86
  // as before the extraction.
86
87
  const message = error instanceof Error ? error.message : "";
87
88
  if (message.endsWith("must be a JSON object")) {
88
- throw new GatewayError("invalid_request", 400, "muse: credential must be a JSON object");
89
+ throw new GatewayError("invalid_request", 400, "muse: credential must be a JSON object (request {requestId}). Wrong key, right door — check the credential 🔑");
89
90
  }
90
91
  if (message.endsWith("is not a valid JSON blob")) {
91
92
  throw new GatewayError(
92
93
  "invalid_request",
93
94
  400,
94
- "muse: credential is not a valid JSON blob; sign in again",
95
+ "muse: credential is not a valid JSON blob (request {requestId}); sign in again. Wrong key, right door — check the credential 🔑",
95
96
  );
96
97
  }
97
98
  throw new GatewayError(
98
99
  "authentication_failed",
99
100
  401,
100
- "muse: credential is missing oauthAccessToken or apiKey; sign in again",
101
+ "muse: credential is missing oauthAccessToken or apiKey (request {requestId}); sign in again. Wrong key, right door — check the credential 🔑",
101
102
  );
102
103
  }
103
104
  }
@@ -137,17 +138,13 @@ class MuseCodeAdapter extends OpenAICompatibleAdapter {
137
138
  candidate: ProviderDispatchTarget,
138
139
  ): Promise<Record<string, string>> {
139
140
  if (context.credential.credential_kind !== "oauth") {
140
- throw new GatewayError(
141
- "invalid_request",
142
- 400,
143
- `muse: OAuth credential required (got credential_kind=${context.credential.credential_kind})`,
144
- );
141
+ assertCredentialKind(context.credential, "oauth", "muse");
145
142
  }
146
143
  if (!context.credential.secret) {
147
144
  throw new GatewayError(
148
145
  "authentication_failed",
149
146
  401,
150
- "muse: OAuth credential secret is required",
147
+ "muse: OAuth credential secret is required (request {requestId}). Wrong key, right door — check the credential 🔑",
151
148
  );
152
149
  }
153
150
  const { apiKey } = decodeMuseCredential(context.credential.secret);
@@ -2,12 +2,14 @@ import { getOpenCodeVersion } from "../../operations/client-versions";
2
2
  import { createHash, randomBytes } from "node:crypto";
3
3
 
4
4
  /**
5
- * The OpenCode fallback client identity, used only while version discovery has
6
- * not resolved yet. The live feed (`opencode/latest/<version>/cli`) wins once
7
- * it is available; upstream accepts a range of releases, so the fallback does
8
- * not need chasing.
5
+ * The pinned reference client identity the parity suite asserts on.
6
+ *
7
+ * The runtime fallback lives in `VERSION_SOURCES.opencode.fallback`
8
+ * (`operations/client-versions.ts`); this mirror exists so the parity suite
9
+ * can pin the exact identity sent before discovery resolves. Keep both in
10
+ * lockstep when the CLI releases.
9
11
  */
10
- export const OPENCODE_CLIENT_VERSION = "2.1.1";
12
+ export const OPENCODE_CLIENT_VERSION = "2.2.5";
11
13
 
12
14
  let lastTimestamp = 0;
13
15
  let sequenceCounter = 0;
@@ -150,17 +150,6 @@ export function isFreeTierZenModel(modelId: string): boolean {
150
150
  return bare.endsWith("-free") || KNOWN_FREE_ZEN_IDS.includes(bare) || bare.toLowerCase().includes("free");
151
151
  }
152
152
 
153
- /**
154
- * The anonymous credential the free tier accepts.
155
- *
156
- * Captured from a real client dispatch and confirmed live 2026-10-14: without
157
- * it a free-model request is refused with 403 `FreeTierError` even when every
158
- * other field is byte-exact, and with it the same request returns 200. It is a
159
- * sentinel the upstream recognizes, not a secret — but it is a credential
160
- * value, so it lives here rather than in the shared fingerprint.
161
- */
162
- const ANONYMOUS_ZEN_BEARER = "public";
163
-
164
153
  /**
165
154
  * The `Accept` a free-tier dispatch advertises.
166
155
  *
@@ -184,9 +173,10 @@ function opencodeDesktopHeaders(
184
173
  context.request_headers?.["x-opencode-project"],
185
174
  context.request_headers?.["x-opencode-session-id"],
186
175
  ),
187
- // The gate reads the anonymous sentinel: a free dispatch with no
188
- // Authorization is refused regardless of headers, tools, or session shape.
189
- ...(freeModel ? { authorization: `Bearer ${ANONYMOUS_ZEN_BEARER}` } : {}),
176
+ // The free-tier gate accepts dispatches with no `Authorization` at all
177
+ // (verified live 2026-11-02: the same captured flow answers 200 both with
178
+ // `Bearer public` and with no auth header, as long as the body/session
179
+ // fingerprint is valid). No anonymous sentinel is sent.
190
180
  // The free-tier gate refuses non-streaming dispatches with 403
191
181
  // FreeTierError, so free models always advertise SSE regardless of the
192
182
  // client's own Accept; billed models keep the JSON default. The value is
@@ -211,27 +201,28 @@ function opencodeDesktopHeaders(
211
201
  * `ToolDefinition`s — every wire codec serializes them, no per-wire patching.
212
202
  */
213
203
  /**
214
- * Every tool the free-tier gate recognises, in the order the client declares
215
- * them, with the client's own description and schema.
204
+ * Every tool the free-tier gate recognises, in the order the real 2.2.0
205
+ * client declares them on the wire (mitm capture 2026-11-02, flow 4), with
206
+ * the client's own description and schema.
216
207
  *
217
208
  * The gate compares a recognised tool's definition, not just its name: a stub
218
209
  * carrying the right name is refused. These are therefore verbatim captures —
219
- * see `./tool-definitions` for provenance.
210
+ * see `./tool-definitions` for provenance. A real dispatch carries exactly
211
+ * these 11 tools (the binary's full `UPt` registry has 14, but the build
212
+ * agent sends only these).
220
213
  */
221
214
  const FREE_AGENT_TOOL_NAMES_ORDERED: readonly string[] = [
222
- "shell",
215
+ "edit",
223
216
  "glob",
224
- "read",
225
217
  "grep",
218
+ "read",
219
+ "shell",
220
+ "skill",
221
+ "subagent",
226
222
  "webfetch",
227
223
  "websearch",
228
224
  "write",
229
- "edit",
230
- "subagent",
231
225
  "execute",
232
- "patch",
233
- "question",
234
- "skill",
235
226
  ];
236
227
 
237
228
  /**
@@ -268,6 +259,8 @@ function ensureFreeAgentRequest(request: CanonicalRequest): CanonicalRequest {
268
259
  name,
269
260
  description: definition?.description ?? name,
270
261
  jsonSchema: (definition?.parameters ?? FREE_AGENT_TOOL_SCHEMA) as ToolDefinition["jsonSchema"],
262
+ // The real client marks every declared tool strict:false on the wire.
263
+ strict: false,
271
264
  };
272
265
  });
273
266
  if (missing.length === 0) return request;
@@ -69,11 +69,11 @@ export function createPerplexityAdapter(fetchImpl?: typeof fetch): ProviderAdapt
69
69
  void _candidate;
70
70
  const query = extractSearchQuery(request);
71
71
  if (!query) {
72
- throw new GatewayError("invalid_request", 400, "Search query is required.");
72
+ throw new GatewayError("invalid_request", 400, "Search query is required (request {requestId}). Check the shape of what you sent — then resend 🪞");
73
73
  }
74
74
  const token = tokenFromContext(context);
75
75
  if (!token) {
76
- throw new GatewayError("authentication_failed", 401, "Missing Perplexity API key");
76
+ throw new GatewayError("authentication_failed", 401, "Missing Perplexity API key (request {requestId}). Wrong key, right door — check the key 🔑");
77
77
  }
78
78
 
79
79
  const fetchFn = (context.outbound_fetch as unknown as typeof fetch | undefined) ?? fetchImpl ?? globalThis.fetch;
@@ -86,6 +86,14 @@ interface QoderModelConfig {
86
86
  * `false` marks the model as billed.
87
87
  */
88
88
  readonly is_free?: boolean;
89
+ /**
90
+ * The reasoning effort to send when the caller did not ask for one. The IDE
91
+ * surface follows the `reasoningEffort` field: a model marked `is_reasoning`
92
+ * may still stream no reasoning unless an effort is present (verified live:
93
+ * Sonus streams thinking only when effort is sent; DeepSeek thinks by
94
+ * default). Set only for models that need the push.
95
+ */
96
+ readonly default_reasoning_effort?: string;
89
97
  }
90
98
 
91
99
  /**
@@ -112,7 +120,7 @@ export const QODER_CN_MODEL_CONFIGS: Readonly<Record<string, QoderModelConfig>>
112
120
  };
113
121
 
114
122
  /**
115
- * Global/intl roster (17 keys), from the donor's `qoder_catalog_intl.json`.
123
+ * Global/intl roster (18 keys), from the donor's `qoder_catalog_intl.json`.
116
124
  *
117
125
  * Note how the shared keys differ from CN — `auto` is non-reasoning here and
118
126
  * `qmodel` carries a 1M window. That is why the rosters are not merged.
@@ -127,19 +135,27 @@ export const QODER_INTL_MODEL_CONFIGS: Readonly<Record<string, QoderModelConfig>
127
135
  fast: { id: "fast", display_name: "Fast", max_input_tokens: 1000000, max_output_tokens: 64_000, is_vl: true, is_reasoning: false },
128
136
  performance: { id: "performance", display_name: "Performance", max_input_tokens: 1000000, max_output_tokens: 64_000, is_vl: true, is_reasoning: false },
129
137
  efficient: { id: "efficient", display_name: "Efficient", max_input_tokens: 200000, max_output_tokens: 64_000, is_vl: true, is_reasoning: false },
130
- smodel: { id: "smodel", display_name: "Sonus", max_input_tokens: 1000000, max_output_tokens: 128_000, is_vl: true, is_free: false, is_reasoning: true },
131
- cmodel: { id: "cmodel", display_name: "Cantus", max_input_tokens: 1000000, max_output_tokens: 128_000, is_vl: true, is_free: false, is_reasoning: true },
138
+ // Sonus/Cantus need an explicit effort on the IDE surface; without it the
139
+ // upstream streams text only, no reasoning (verified live). DeepSeek-family
140
+ // models think by default and are left untouched.
141
+ smodel: { id: "smodel", display_name: "Sonus", max_input_tokens: 1000000, max_output_tokens: 128_000, is_vl: true, is_free: false, is_reasoning: true, default_reasoning_effort: "high" },
142
+ cmodel: { id: "cmodel", display_name: "Cantus", max_input_tokens: 1000000, max_output_tokens: 128_000, is_vl: true, is_free: false, is_reasoning: true, default_reasoning_effort: "high" },
132
143
  qmodel_38max: { id: "qmodel_38max", display_name: "Qwen3.8-Max", max_input_tokens: 1000000, max_output_tokens: 131_072, is_vl: true, is_reasoning: true },
133
144
  qfmodel: { id: "qfmodel", display_name: "Qwen3.8-Flash", max_input_tokens: 1000000, max_output_tokens: 131_072, is_vl: true, is_free: true, is_reasoning: true },
134
145
  qmodel_latest: { id: "qmodel_latest", display_name: "Qwen3.7-Max", max_input_tokens: 1000000, max_output_tokens: 131_072, is_vl: true, is_reasoning: false },
135
146
  qmodel: { id: "qmodel", display_name: "Qwen3.7-Plus", max_input_tokens: 1000000, max_output_tokens: 131_072, is_vl: true, is_reasoning: false },
136
- kmodel_latest: { id: "kmodel_latest", display_name: "Kimi-K3", max_input_tokens: 1000000, max_output_tokens: 131_072, is_vl: true, is_free: false, is_reasoning: false },
147
+ kmodel_latest: { id: "kmodel_latest", display_name: "Kimi-K3.1-Pro", max_input_tokens: 1000000, max_output_tokens: 131_072, is_vl: true, is_free: false, is_reasoning: false },
137
148
  kmodel: { id: "kmodel", display_name: "Kimi-K2.8-Preview", max_input_tokens: 1000000, max_output_tokens: 65_536, is_vl: true, is_free: false, is_reasoning: false },
138
149
  gmodel: { id: "gmodel", display_name: "GLM-5.3", max_input_tokens: 1000000, max_output_tokens: 131_072, is_vl: true, is_free: false, is_reasoning: true },
139
150
  gfmodel: { id: "gfmodel", display_name: "GLM-5.3-Flash", max_input_tokens: 1000000, max_output_tokens: 131_072, is_vl: true, is_reasoning: true },
140
151
  dmodel: { id: "dmodel", display_name: "DeepSeek-V4-Pro", max_input_tokens: 1000000, max_output_tokens: 384_000, is_vl: true, is_free: false, is_reasoning: true },
141
- dfmodel: { id: "dfmodel", display_name: "DeepSeek-Flash", max_input_tokens: 1000000, max_output_tokens: 384_000, is_vl: true, is_free: true, is_reasoning: true },
142
- mmodel: { id: "mmodel", display_name: "MiniMax-M3", max_input_tokens: 1000000, max_output_tokens: 512_000, is_vl: true, is_free: false, is_reasoning: false },
152
+ // Live roster (2026-11-01) reports dfmodel as billed (is_free false); it was
153
+ // previously marked free. Display names are corrected from the same capture.
154
+ dfmodel: { id: "dfmodel", display_name: "DeepSeek-Flash", max_input_tokens: 1000000, max_output_tokens: 384_000, is_vl: true, is_free: false, is_reasoning: true },
155
+ mmodel: { id: "mmodel", display_name: "MiniMax-M3.1-Flash", max_input_tokens: 1000000, max_output_tokens: 512_000, is_vl: true, is_free: false, is_reasoning: false },
156
+ // `lite` is the free-tier plan package: same 1M window and multimodal input
157
+ // as `qmodel` (Qwen3.7-Plus), capped at 64k output, free tier.
158
+ lite: { id: "lite", display_name: "Lite-Qwen-3.7-Plus", max_input_tokens: 1000000, max_output_tokens: 65_536, is_vl: true, is_free: true, is_reasoning: false },
143
159
  };
144
160
 
145
161
  /**
@@ -211,13 +227,24 @@ export function qoderVirtualModels(
211
227
  // substitutes the shared unfiled fallback for null — the same marker a
212
228
  // hand-added row gets.
213
229
  ctx: target.contextLimit ?? null,
214
- ...(target.outputLimit === null ? {} : { out: target.outputLimit }),
215
- vision: target.modalities.input.includes("image"),
230
+ // A virtual override wins; otherwise inherit the target's ceiling.
231
+ ...(virtual.outputLimit !== undefined
232
+ ? { out: virtual.outputLimit }
233
+ : target.outputLimit === null
234
+ ? {}
235
+ : { out: target.outputLimit }),
236
+ vision: virtual.vision ?? target.modalities.input.includes("image"),
216
237
  reasoning: target.reasoning ?? false,
217
238
  toolCall: target.toolCall ?? false,
218
239
  // The same model under its caller-facing name belongs to the same tier,
219
- // so the pill follows the roster row rather than being restated.
220
- ...(target.freeTier === undefined ? {} : { free: target.freeTier, freeTier: target.freeTier }),
240
+ // so the pill follows the roster row rather than being restated —
241
+ // unless the virtual card explicitly overrides it (`lite` is a free
242
+ // plan package over a billed slot).
243
+ ...(virtual.free !== undefined
244
+ ? { free: virtual.free, freeTier: virtual.free }
245
+ : target.freeTier === undefined
246
+ ? {}
247
+ : { free: target.freeTier, freeTier: target.freeTier }),
221
248
  }),
222
249
  );
223
250
  }
@@ -272,11 +299,27 @@ export function mapQoderCatalogResponse(raw: unknown): QoderCatalogEntry[] {
272
299
  return entries;
273
300
  }
274
301
 
302
+ /**
303
+ * The static roster's stated output ceiling for one slot, when the roster
304
+ * states one.
305
+ *
306
+ * Upstream's live `/model/list` rows report no output limit, so a discovered
307
+ * row cannot read one from the wire. Inheriting the roster's number keeps the
308
+ * discovered row's `outputLimit` honest — without it `defineModel` falls back
309
+ * to a base-catalog/majority vote that can sit below the roster's ceiling
310
+ * (e.g. `qwen3.7-plus` votes 64k while the roster says 131k).
311
+ */
312
+ function qoderRosterMaxOutputFor(providerId: string, key: string): number | undefined {
313
+ const configs = providerId === "qodercn" ? QODER_CN_MODEL_CONFIGS : QODER_INTL_MODEL_CONFIGS;
314
+ return configs[key]?.max_output_tokens;
315
+ }
316
+
275
317
  /** Projects one live catalog row onto a Cartethyia catalog row. */
276
318
  export function qoderCatalogModel(
277
319
  entry: QoderCatalogEntry,
278
320
  providerId: string,
279
321
  ): ModelDefinition {
322
+ const rosterOutput = qoderRosterMaxOutputFor(providerId, entry.key);
280
323
  return defineModel({
281
324
  id: entry.key,
282
325
  providerId,
@@ -293,6 +336,9 @@ export function qoderCatalogModel(
293
336
  // a free row bills zero per token and belongs to the provider's free tier.
294
337
  free: entry.isFree,
295
338
  freeTier: entry.isFree,
339
+ // The live row carries no output ceiling, so inherit the static roster's
340
+ // stated one when it exists (see `qoderRosterMaxOutputFor`).
341
+ ...(rosterOutput === undefined ? {} : { out: rosterOutput }),
296
342
  });
297
343
  }
298
344
 
@@ -353,6 +399,14 @@ interface CachedRoster {
353
399
  /** Per-realm live-list cache; keyed by realm, not by account. */
354
400
  const discoveryCache = new Map<QoderRealm, CachedRoster>();
355
401
 
402
+ /**
403
+ * Per-realm max-context window per roster key, taken from the live list's
404
+ * `context_config` (the widest selectable window). This is what dispatch reads
405
+ * to send `model_config.max_input_tokens`; without it the request would use the
406
+ * static roster's *currently active* window (often 180k) instead of the max.
407
+ */
408
+ const discoveryMaxContextByKey = new Map<QoderRealm, Map<string, number>>();
409
+
356
410
  /**
357
411
  * Drops one realm's cached list.
358
412
  *
@@ -361,8 +415,21 @@ const discoveryCache = new Map<QoderRealm, CachedRoster>();
361
415
  * previous assertion left behind.
362
416
  */
363
417
  export function resetQoderDiscoveryCache(realm?: QoderRealm): void {
364
- if (realm === undefined) discoveryCache.clear();
365
- else discoveryCache.delete(realm);
418
+ if (realm === undefined) {
419
+ discoveryCache.clear();
420
+ discoveryMaxContextByKey.clear();
421
+ } else {
422
+ discoveryCache.delete(realm);
423
+ discoveryMaxContextByKey.delete(realm);
424
+ }
425
+ }
426
+
427
+ /**
428
+ * The widest selectable context window per roster key for one realm, from the
429
+ * live model list. Empty when discovery has not succeeded for that realm yet.
430
+ */
431
+ export function qoderDiscoveredMaxContexts(realm: QoderRealm): ReadonlyMap<string, number> {
432
+ return discoveryMaxContextByKey.get(realm) ?? new Map();
366
433
  }
367
434
 
368
435
  export async function discoverQoderModels(input: {
@@ -389,7 +456,7 @@ export async function discoverQoderModels(input: {
389
456
  throw new GatewayError(
390
457
  "authentication_failed",
391
458
  401,
392
- "Qoder model discovery requires an account credential.",
459
+ "Qoder model discovery requires an account credential (request {requestId}). Wrong key, right door — check the credential 🔑",
393
460
  { providerId, realm: input.realm },
394
461
  );
395
462
  }
@@ -398,7 +465,7 @@ export async function discoverQoderModels(input: {
398
465
  throw new GatewayError(
399
466
  "invalid_request",
400
467
  400,
401
- "Qoder model discovery requires a validated outbound fetch",
468
+ "Qoder model discovery requires a validated outbound fetch (request {requestId}). No bridge to cross — configure a pool first 🏗️",
402
469
  { providerId, realm: input.realm },
403
470
  );
404
471
  }
@@ -409,6 +476,11 @@ export async function discoverQoderModels(input: {
409
476
  const url = `${host}${QODER_MODELS_PATH}`;
410
477
  try {
411
478
  const headers = await input.headers(url, signedBody);
479
+ // GET, not POST: upstream refuses POST on the model-list surface (400
480
+ // "Request method 'POST' not supported"). The signature covers the body,
481
+ // so the body must accompany the GET. The gateway's validated egress
482
+ // dials GET+body through the Node http(s) client (the runtime fetch
483
+ // rejects GET bodies), which is exactly the shape the donor client sends.
412
484
  const response = await fetcher(url, {
413
485
  method: "GET",
414
486
  headers: headers as unknown as HeadersInit,
@@ -429,10 +501,53 @@ export async function discoverQoderModels(input: {
429
501
  lastError = new Error("Qoder model list returned no usable models");
430
502
  continue;
431
503
  }
432
- const models = enabled.map((entry) => qoderCatalogModel(entry, providerId));
504
+ const models = enabled.flatMap((entry) => {
505
+ // Publish caller-facing virtual ids (`sonus`, `lite-qwen-3.7-plus`, …)
506
+ // instead of the opaque roster keys (`smodel`, `qmodel`, …). A discovery
507
+ // row must look like what the UI offers and what a caller can type;
508
+ // keeping the raw key here made live-synced rows appear under names
509
+ // nobody can route to by display, and hid overridden cards like `lite`.
510
+ // More than one virtual may share a slot (`lite-qwen-3.7-plus` and
511
+ // `qwen3.7-plus` both dispatch to `qmodel`), so every match on the slot
512
+ // is published.
513
+ const virtuals = QODER_VIRTUAL_MODELS.filter((virtual) => virtual.target === entry.key);
514
+ if (virtuals.length === 0) return [qoderCatalogModel(entry, providerId)];
515
+ const rosterOutput = qoderRosterMaxOutputFor(providerId, entry.key);
516
+ return virtuals.map((virtual) =>
517
+ defineModel({
518
+ id: virtual.id,
519
+ providerId,
520
+ wireFamily: "chat",
521
+ endpoint: QODER_CHAT_PATH,
522
+ ctx: entry.maxInputTokens ?? null,
523
+ vision: virtual.vision ?? entry.isVl,
524
+ reasoning: entry.isReasoning,
525
+ // A virtual override wins; otherwise inherit the static roster's
526
+ // ceiling for the slot instead of a base-catalog/majority estimate
527
+ // (see `qoderRosterMaxOutputFor`).
528
+ ...(virtual.outputLimit !== undefined
529
+ ? { out: virtual.outputLimit }
530
+ : rosterOutput === undefined
531
+ ? {}
532
+ : { out: rosterOutput }),
533
+ free: virtual.free ?? entry.isFree,
534
+ freeTier: virtual.free ?? entry.isFree,
535
+ }),
536
+ );
537
+ });
538
+ // Index the widest selectable window per key (from `context_config`,
539
+ // already computed into `maxInputTokens` by `mapQoderCatalogResponse`) so
540
+ // dispatch can send the maximum rather than the currently active one.
541
+ const maxCtx = new Map<string, number>();
542
+ for (const entry of enabled) {
543
+ if (entry.maxInputTokens !== undefined && entry.key.length > 0) {
544
+ maxCtx.set(entry.key, entry.maxInputTokens);
545
+ }
546
+ }
433
547
  // Cached only after a host actually answered: a failure must stay
434
548
  // retryable on the next call rather than being frozen for five minutes.
435
549
  discoveryCache.set(input.realm, { at: Date.now(), models });
550
+ discoveryMaxContextByKey.set(input.realm, maxCtx);
436
551
  return models;
437
552
  } catch (error) {
438
553
  lastError = error;
@@ -441,9 +556,9 @@ export async function discoverQoderModels(input: {
441
556
  throw new GatewayError(
442
557
  "transport_unavailable",
443
558
  502,
444
- `Qoder model discovery failed for ${input.realm}: ${
559
+ `Qoder model discovery failed for ${input.realm} (request {requestId}): ${
445
560
  lastError instanceof Error ? lastError.message : "unknown error"
446
- }`,
561
+ }. The connection ghosted us — come back in a moment 👻`,
447
562
  { providerId, realm: input.realm },
448
563
  "upstream",
449
564
  );
@@ -175,15 +175,18 @@ async function tryClaimDaily(
175
175
  if (campaigns === null) {
176
176
  return { state: "error", amount: 0, error: "Campaign list unavailable." };
177
177
  }
178
- for (const campaign of campaigns) {
178
+ // Claim a `CLAIMABLE` CREDITS-kind campaign. Promo rows (`VIEW_DETAILS`,
179
+ // no benefit) are not credit grants and must not be claimed — claiming one
180
+ // spends the account's daily slot for nothing and makes the sweep report a
181
+ // misleading `claimed` with 0 credits.
182
+ const claimable = campaigns.filter((c) => {
183
+ if (String(c["claim_status"] ?? "").toUpperCase() !== "CLAIMABLE") return false;
184
+ const kind = String(asRecord(c["benefit"])?.["kind"] ?? "").toUpperCase();
185
+ const action = String(c["action_type"] ?? "").toUpperCase();
186
+ return kind === "CREDITS" || action === "CLAIM_BENEFIT";
187
+ });
188
+ for (const campaign of claimable) {
179
189
  const benefit = asRecord(campaign["benefit"]);
180
- const kind = String(benefit?.["kind"] ?? "").toUpperCase();
181
- if (kind !== "CREDITS" && kind !== "") continue;
182
- const actionType = String(campaign["action_type"] ?? "");
183
- if (actionType !== "" && actionType !== "CLAIM_BENEFIT") continue;
184
- const claimStatus = String(campaign["claim_status"] ?? "").toUpperCase();
185
- if (claimStatus === "CLAIMED") return { state: "already_claimed", amount: 0, error: null };
186
- if (claimStatus !== "CLAIMABLE") continue;
187
190
  const campaignId = String(campaign["campaign_id"] ?? "");
188
191
  if (!campaignId) continue;
189
192
  const claim = await claimWithHeaders(base, campaignId, headers, fetcher);
@@ -195,6 +198,18 @@ async function tryClaimDaily(
195
198
  if (claim.state === "not_eligible") return { state: "not_eligible", amount: 0, error: null };
196
199
  return { state: "error", amount: 0, error: "Campaign claim failed." };
197
200
  }
201
+ // Nothing claimable. Only a CREDITS-kind campaign that is `CLAIMED` proves
202
+ // today's credit was already taken; a claimed promo (`VIEW_DETAILS`, no
203
+ // benefit) says nothing about the daily grant, so it must not be reported
204
+ // as already_claimed. When the CREDITS campaign is absent entirely, the
205
+ // daily grant is simply not present (or hidden) for this account.
206
+ const anyCreditsClaimed = campaigns.some((c) => {
207
+ if (String(c["claim_status"] ?? "").toUpperCase() !== "CLAIMED") return false;
208
+ const kind = String(asRecord(c["benefit"])?.["kind"] ?? "").toUpperCase();
209
+ const action = String(c["action_type"] ?? "").toUpperCase();
210
+ return kind === "CREDITS" || action === "CLAIM_BENEFIT";
211
+ });
212
+ if (anyCreditsClaimed) return { state: "already_claimed", amount: 0, error: null };
198
213
  return { state: "unavailable", amount: 0, error: "No daily CREDITS campaign claimable." };
199
214
  }
200
215
 
@@ -224,6 +239,9 @@ async function probeDailyClaim(
224
239
  ): Promise<{ result: { state: DailyCheckinState; amount: number; error: string | null }; identity: QoderNativeIdentity | null }> {
225
240
  const basic = await tryClaimDaily(base, campaignHeaders(credential), fetcher);
226
241
  if (basic.state === "claimed" || basic.state === "already_claimed") {
242
+ // `tryClaimDaily` only claims CREDITS-kind campaigns, so a `claimed` or
243
+ // `already_claimed` basic result is already about the daily credit grant —
244
+ // terminal, no identity probe needed.
227
245
  return { result: basic, identity: null };
228
246
  }
229
247
  if (basic.state === "unavailable") {
@@ -2,15 +2,16 @@
2
2
  * Persistence for the Qoder native machine identity (UMID) bound to an
3
3
  * account.
4
4
  *
5
- * The daily 100-credit check-in grant is device-scoped upstream, and the
6
- * server accepts only genuine, non-VM device identities. A device identity is
7
- * a deterministic function of a seed file (`~/.config/.locale_cfg`), so each
8
- * account gets its own minted seed — its own "device". The bound record lives
9
- * in `provider_accounts.native_identity` (plain JSON, not a secret).
5
+ * Same-account probes found that daily 100-credit grant visibility does not
6
+ * depend on machine headers. This store preserves native identities and seeds
7
+ * for deterministic replay and other surfaces that may use device identity.
8
+ * A native identity is a deterministic function of a seed file
9
+ * (`~/.config/.locale_cfg`). The bound record lives in
10
+ * `provider_accounts.native_identity` (plain JSON, not a secret).
10
11
  *
11
12
  * Uniqueness of the device is enforced by the partial unique index on the
12
- * extracted `machineToken`; this store keeps the JS side of that invariant
13
- * and surfaces the DB constraint as the backstop.
13
+ * extracted `machineToken`; this store keeps the JS side of that internal
14
+ * identity-allocation invariant and surfaces the DB constraint as the backstop.
14
15
  */
15
16
  import { and, eq, isNotNull, ne } from "drizzle-orm";
16
17
  import type { CartethyiaDatabase } from "../../../persistence/db-handle";
@@ -30,7 +31,7 @@ export interface QoderNativeIdentityStore {
30
31
  get(providerId: string, accountId: string): Promise<NativeIdentityRecord | undefined>;
31
32
  /**
32
33
  * Machine tokens already bound to **other** accounts, so a caller can mint
33
- * a fresh device without colliding. A bound token is a physical device, and
34
+ * a fresh identity without colliding. A bound token represents a device identity, and
34
35
  * the unique index that backs this spans all `provider_accounts` rows. The
35
36
  * account's own bound token is not "occupied" — reusing one's own device is
36
37
  * the point.