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
@@ -8,14 +8,16 @@
8
8
  * the same endpoints as the upstream client.
9
9
  *
10
10
  * The issued token is used directly at dispatch: `dt-...` tokens skip the
11
- * PAT→jobToken exchange. There is no working refresh grant —
12
- * the upstream refresh endpoint answers 403 for this flow — so no refresher
13
- * is registered and the token is used exactly as issued until the operator
14
- * re-runs login. The account's Qoder user id and machine id travel in
15
- * `auth_state`, because the token itself carries neither and every dispatch
16
- * needs both for COSY signing.
11
+ * PAT→jobToken exchange. Refresh uses the deviceToken refresh endpoint
12
+ * (`POST /api/v1/deviceToken/refresh` with `{refresh_token, machine_id,
13
+ * machine_token}`), verified live 2026-11-02 — it returns a fresh `dt-…`
14
+ * and a rotated `drt-…`. The account's Qoder user id and machine id travel
15
+ * in `auth_state`, because the token itself carries neither and every
16
+ * dispatch needs both for COSY signing.
17
17
  */
18
18
  import { createHash, randomBytes, randomUUID } from "node:crypto";
19
+ import { execSync } from "node:child_process";
20
+ import { readFileSync } from "node:fs";
19
21
  import { QODER_REALMS, type QoderRealm } from "./qoder-realm";
20
22
  import {
21
23
  base64UrlEncode,
@@ -28,7 +30,7 @@ import type {
28
30
  OAuthDeviceStartResult,
29
31
  OAuthExchangeResult,
30
32
  } from "../../authentication/oauth-flow-store";
31
- import type { OAuthTokenRefreshResult } from "../../authentication/oauth-refresh-service";
33
+ import type { OAuthTokenRefreshResult, OAuthRefreshContext } from "../../authentication/oauth-refresh-service";
32
34
  import { OAuthDeviceFlow } from "../../authentication/oauth-device-flow";
33
35
 
34
36
  /** One Qoder account profile, as much of it as the gateway uses. */
@@ -109,9 +111,7 @@ export function readQoderUserInfo(body: Record<string, unknown>): QoderUserInfo
109
111
  }
110
112
 
111
113
  /** Global/intl endpoints. Kept as the default realm's values, unchanged. */
112
- export const QODER_LOGIN_URL = "https://qoder.com/device/selectAccounts" as const;
113
114
  export const QODER_DEVICE_TOKEN_URL = "https://openapi.qoder.sh/api/v1/deviceToken/poll" as const;
114
- export const QODER_USERINFO_URL = "https://openapi.qoder.sh/api/v1/userinfo" as const;
115
115
 
116
116
  /**
117
117
  * Per-realm device endpoints, derived from `QODER_REALMS`.
@@ -221,6 +221,82 @@ function parseDeviceState(value: string | undefined): QoderDeviceState | undefin
221
221
  return { verifier, machineId };
222
222
  }
223
223
 
224
+ /**
225
+ * Random PKCE verifier in the exact alphabet the desktop IDE uses.
226
+ *
227
+ * The IDE's `startDeviceFlow` draws the verifier from
228
+ * `ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~`. Using
229
+ * the same alphabet keeps the login byte-for-byte identical to the desktop
230
+ * client, which the approval server may use to fingerprint the requesting
231
+ * client. Length is fixed at 64 — RFC 7636 §4.1 requires [43,128]; a fixed
232
+ * length avoids a non-cryptographic length while the content comes entirely
233
+ * from the CSPRNG (`randomBytes`) below.
234
+ */
235
+ function qoderIdeVerifier(): string {
236
+ const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~";
237
+ // RFC 7636 §4.1: verifier length must be within [43,128]; fixed here. Content
238
+ // comes from the CSPRNG (randomBytes) below. `67` (alphabet length) does not
239
+ // divide 256, so the `%` mapping has a negligible modulo bias (~21/256 per
240
+ // char) — irrelevant for a 64-char secret with ~388 bits of input entropy.
241
+ const length = 64;
242
+ const bytes = randomBytes(length);
243
+ let out = "";
244
+ for (let i = 0; i < length; i += 1) {
245
+ out += alphabet[bytes[i]! % alphabet.length];
246
+ }
247
+ return out;
248
+ }
249
+
250
+ /**
251
+ * The hardware machine id the desktop IDE reports for the device flow.
252
+ *
253
+ * The IDE's `getMachineId` is platform-hardware-derived: Windows reads
254
+ * `MachineGuid` from `HKLM\SOFTWARE\Microsoft\Cryptography`, macOS reads the
255
+ * `IOPlatformUUID` from IORegistry, Linux reads `/etc/machine-id`. The device
256
+ * login sends it as `machine_id`, and the upstream correlates the approved
257
+ * device to the account. A random UUID instead registers a different device,
258
+ * which is the observable difference between an IDE login that chats and a
259
+ * gateway login that is refused with quota errors.
260
+ *
261
+ * Returns the hardware id when it can be read, else `""` so callers fall back
262
+ * to a random UUID exactly like the IDE does on an unreadable machine.
263
+ */
264
+ export function qoderHardwareMachineId(): string {
265
+ try {
266
+ if (process.platform === "win32") {
267
+ const out = execSync('reg query "HKLM\\SOFTWARE\\Microsoft\\Cryptography" /v MachineGuid', {
268
+ encoding: "utf8",
269
+ stdio: ["ignore", "pipe", "ignore"],
270
+ timeout: 5000,
271
+ });
272
+ const m = /MachineGuid\s+REG_SZ\s+([0-9a-fA-F-]+)/.exec(out);
273
+ return m?.[1]?.trim() ?? "";
274
+ }
275
+ if (process.platform === "darwin") {
276
+ const out = execSync("ioreg -rd1 -c IOPlatformExpertDevice", {
277
+ encoding: "utf8",
278
+ stdio: ["ignore", "pipe", "ignore"],
279
+ timeout: 5000,
280
+ });
281
+ const m = /"IOPlatformUUID"\s*=\s*"([^"]+)"/.exec(out);
282
+ return m?.[1]?.trim() ?? "";
283
+ }
284
+ if (process.platform === "linux") {
285
+ for (const p of ["/etc/machine-id", "/var/lib/dbus/machine-id"]) {
286
+ try {
287
+ const v = readFileSync(p, "utf8").trim();
288
+ if (v) return v;
289
+ } catch {
290
+ // try the next candidate
291
+ }
292
+ }
293
+ }
294
+ } catch {
295
+ // fall through to the caller's random fallback
296
+ }
297
+ return "";
298
+ }
299
+
224
300
  export class QoderOAuthClient extends OAuthDeviceFlow {
225
301
  override readonly supportsDeviceCode = true;
226
302
  override readonly supportsBrowserCode = false;
@@ -254,25 +330,35 @@ export class QoderOAuthClient extends OAuthDeviceFlow {
254
330
  override async startDeviceAuth(
255
331
  _context?: OAuthDeviceFlowContext,
256
332
  ): Promise<OAuthDeviceStartResult> {
257
- const verifier = base64UrlEncode(randomBytes(32));
333
+ // 1:1 with the desktop IDE device flow (unpacked worker `startDeviceFlow`):
334
+ // the verifier is random from the IDE's own alphabet (`A-Za-z0-9-._~`,
335
+ // 43–128 chars), the challenge is PKCE S256, the nonce is a full UUID
336
+ // (dashes included), and the client id is ALWAYS sent — intl uses the
337
+ // same id the realm table carries, CN uses its own. Sending no client id
338
+ // (or stripping the UUID dashes) makes the approval screen classify the
339
+ // device differently and can produce an account the model server later
340
+ // refuses with quota/entitlement errors.
341
+ //
342
+ // The machine id defaults to the hardware id (same source the IDE reads),
343
+ // but an operator may pass `machine_id`/`machine_token` through
344
+ // `context.parameters` to reuse a device identity the IDE already holds
345
+ // (e.g. `aicoding.loginMachineIdentity.v1.qoder.local`) — the upstream
346
+ // correlates the approved device to the account, so matching the IDE's
347
+ // device is how a gateway login chats on the same account the IDE does.
348
+ const overrideMachineId = nonEmptyTrimmedString(_context?.parameters?.["machine_id"]);
349
+ const overrideMachineToken = nonEmptyTrimmedString(_context?.parameters?.["machine_token"]);
350
+ const verifier = qoderIdeVerifier();
258
351
  const challenge = base64UrlEncode(createHash("sha256").update(verifier).digest());
259
- // CN carries a dashed nonce and additionally states the client id and
260
- // native redirect; intl stays on the 32-hex nonce and the two params it
261
- // has always sent. Both shapes are upstream fact from the donor, and only
262
- // the CN branch is new, so the working intl URL is untouched.
263
- const nonce = this.realm === "cn" ? randomUUID() : randomUUID().replaceAll("-", "");
264
- const machineId = randomUUID();
352
+ const nonce = randomUUID();
353
+ const machineId = overrideMachineId ?? (qoderHardwareMachineId() || randomUUID());
265
354
  const params = new URLSearchParams({
266
355
  challenge,
267
356
  challenge_method: "S256",
268
- machine_id: machineId,
269
357
  nonce,
358
+ machine_id: machineId,
359
+ client_id: QODER_REALMS[this.realm].clientId,
270
360
  });
271
- if (this.realm === "cn") {
272
- const config = QODER_REALMS.cn;
273
- params.set("client_id", config.clientId);
274
- params.set("redirect_uri", config.redirectUri);
275
- }
361
+ if (overrideMachineToken !== undefined) params.set("machine_token", overrideMachineToken);
276
362
  return {
277
363
  verificationUri: `${qoderOAuthUrls(this.realm).loginUrl}?${params.toString()}`,
278
364
  // This flow shows no user code: approval happens against the
@@ -395,10 +481,64 @@ export class QoderOAuthClient extends OAuthDeviceFlow {
395
481
  }
396
482
 
397
483
  override async refresh(
398
- _refreshToken: string,
399
- _signal?: AbortSignal,
484
+ refreshToken: string,
485
+ signal?: AbortSignal,
486
+ context?: OAuthRefreshContext,
400
487
  ): Promise<OAuthTokenRefreshResult> {
401
- throw new Error("Qoder does not support token refresh; sign in again");
488
+ // The deviceToken refresh endpoint is the one working refresh grant for this
489
+ // flow (verified live 2026-11-02: POST /api/v1/deviceToken/refresh with
490
+ // `{refresh_token, machine_id, machine_token}` returns 200 with a fresh
491
+ // `device_token` + rotated `refresh_token`). The abstract OAuth refresh
492
+ // endpoint answers 403 for this flow, so we post to the deviceToken one.
493
+ const authState = record(context?.auth_state);
494
+ const machineId = authState === undefined ? undefined : nonEmptyTrimmedString(authState.machineId);
495
+ if (machineId === undefined) {
496
+ throw new Error("Qoder refresh requires auth_state.machineId");
497
+ }
498
+ const response = await this.fetchFn(
499
+ `${qoderOAuthUrls(this.realm).deviceTokenUrl}/refresh`,
500
+ {
501
+ method: "POST",
502
+ headers: {
503
+ "content-type": "application/json",
504
+ accept: "application/json",
505
+ "user-agent": "Go-http-client/2.0",
506
+ },
507
+ body: JSON.stringify({
508
+ refresh_token: refreshToken,
509
+ machine_id: machineId,
510
+ machine_token: machineId,
511
+ }),
512
+ ...(signal === undefined ? {} : { signal }),
513
+ },
514
+ );
515
+ const raw = await response.text();
516
+ let payload: unknown;
517
+ try {
518
+ payload = JSON.parse(raw);
519
+ } catch {
520
+ throw new Error(`Qoder token refresh returned invalid JSON (${response.status})`);
521
+ }
522
+ if (!response.ok) {
523
+ const message = record(payload);
524
+ throw new Error(
525
+ nonEmptyTrimmedString(message?.message) ??
526
+ nonEmptyTrimmedString(message?.errorMessage) ??
527
+ `Qoder token refresh failed (${response.status})`,
528
+ );
529
+ }
530
+ const body = record(payload);
531
+ const deviceToken = body === undefined ? undefined : nonEmptyTrimmedString(body.device_token);
532
+ if (deviceToken === undefined) {
533
+ throw new Error("Qoder token refresh response omitted device_token");
534
+ }
535
+ const nextRefresh = nonEmptyTrimmedString(body?.refresh_token);
536
+ const expiresAt = new Date(parseQoderExpiry(body?.expires_at, body?.expires_in));
537
+ return {
538
+ access: deviceToken,
539
+ ...(nextRefresh === undefined ? {} : { refresh: nextRefresh }),
540
+ expiresAt,
541
+ };
402
542
  }
403
543
  }
404
544
 
@@ -1,8 +1,10 @@
1
1
  // Qoder quota.
2
2
  //
3
- // `GET /api/v2/quota/usage` on the **openapi** host, with a plain bearer token
4
- // — no COSY signature on this surface, which is the one place the business API
5
- // differs from inference.
3
+ // `GET /api/v2/quota/usage` on the **openapi** host, with a bearer token — no
4
+ // COSY signature on this surface, which is the one place the business API
5
+ // differs from inference. Device tokens (`dt-…`) are used directly; a PAT
6
+ // (`pt-…`) is exchanged for a job token (`jt-…`) first, because the business
7
+ // API rejects a raw PAT with 401 `TOKEN_EXPIRE`.
6
8
  //
7
9
  // The response carries two quota objects and they are not interchangeable:
8
10
  //
@@ -36,6 +38,7 @@ import {
36
38
  import type { QuotaCollectionContext } from "../../quota/quota-support";
37
39
  import { log } from "../../../observability/logger";
38
40
  import { QODER_REALMS, type QoderRealm } from "./qoder-realm";
41
+ import { isQoderPat, resolveQoderAuth } from "./qoder";
39
42
 
40
43
  /** Paths on the realm's openapi host. */
41
44
  const QUOTA_PATH = "/api/v2/quota/usage";
@@ -75,7 +78,29 @@ function creditWindow(
75
78
  ): ProviderQuotaWindow | null {
76
79
  if (remaining === null && limit === null) return null;
77
80
  const used = remaining !== null && limit !== null ? limit - remaining : null;
78
- return percentWindow(kind, label, null, resetsAt, used, limit, "pooled");
81
+ // Give pooled windows a percentage so the console can render a real bar and
82
+ // tone instead of an uncolored "quota-only" row (which is what a null
83
+ // percent produced: every Qoder account was grey, and "enable available
84
+ // accounts" could never match a window). A ceiling of 0 with nothing left is
85
+ // a spent balance — report 0% remaining rather than a null percent.
86
+ let usedPercent: number | null = null;
87
+ if (remaining !== null && limit !== null) {
88
+ if (limit > 0) usedPercent = ((limit - remaining) / limit) * 100;
89
+ else if (remaining <= 0) usedPercent = 100;
90
+ }
91
+ return percentWindow(kind, label, usedPercent, resetsAt, used, limit, "pooled");
92
+ }
93
+
94
+ /**
95
+ * The business API answers "never resets" with a far-future sentinel
96
+ * (`9999-12-31T00:00:00.000Z`). Keeping it on the wire makes the console show
97
+ * a 7978-year countdown. Any date at or beyond year 9999 means no reset.
98
+ */
99
+ function normalizeQoderReset(resetsAt: string | null): string | null {
100
+ if (resetsAt === null) return null;
101
+ const date = new Date(resetsAt);
102
+ if (!Number.isFinite(date.getTime())) return null;
103
+ return date.getUTCFullYear() >= 9999 ? null : resetsAt;
79
104
  }
80
105
 
81
106
  /**
@@ -103,11 +128,28 @@ export async function fetchQoderQuota(
103
128
 
104
129
  let payload: unknown;
105
130
  try {
131
+ // A PAT (`pt-…`) is not an active bearer on the business API: it is a
132
+ // minted credential that must be exchanged for a short-lived job token
133
+ // (`jt-…`) first. Verified live 2026-11-02: a raw `pt-…` bearer answers
134
+ // 401 `TOKEN_EXPIRE` while the exchanged `jt-…` answers 200. Device
135
+ // tokens (`dt-…`) are used directly. A failed exchange — expired/revoked
136
+ // PAT — is an unreadable balance, not an error that cools the account.
137
+ let bearer = credential;
138
+ if (isQoderPat(credential)) {
139
+ const auth = await resolveQoderAuth({
140
+ secret: credential,
141
+ authState: raw,
142
+ signal: AbortSignal.timeout(15_000),
143
+ fetcher,
144
+ realm,
145
+ });
146
+ bearer = auth.securityOauthToken;
147
+ }
106
148
  const response = await fetcher(url, {
107
149
  method: "GET",
108
150
  headers: {
109
151
  accept: "application/json",
110
- authorization: `Bearer ${credential}`,
152
+ authorization: `Bearer ${bearer}`,
111
153
  "user-agent": config.userAgent,
112
154
  },
113
155
  });
@@ -148,7 +190,7 @@ export async function fetchQoderQuota(
148
190
  return { source: "qoder", plan: null, windows: [], error: null };
149
191
  }
150
192
 
151
- const resetsAt = isoDate(root["expiresAt"] ?? root["expireAt"] ?? root["expires_at"]);
193
+ const resetsAt = normalizeQoderReset(isoDate(root["expiresAt"] ?? root["expireAt"] ?? root["expires_at"]));
152
194
  const windows: ProviderQuotaWindow[] = [];
153
195
  const plan = creditWindow("qoder:plan", "Plan credits", number(userQuota?.["remaining"]), number(userQuota?.["total"]), resetsAt);
154
196
  if (plan) windows.push(plan);
@@ -39,6 +39,25 @@ export const QODER_CHAT_PATH =
39
39
  "/algo/api/v2/service/pro/sse/agent_chat_generation?FetchKeys=llm_model_result&AgentId=agent_common&Encode=1";
40
40
  export const QODER_MODELS_PATH = "/algo/api/v2/model/list?Encode=1";
41
41
 
42
+ /**
43
+ * The modern model-server surface used by the IDE / QoderWake (OpenAI-shaped
44
+ * chunks, plain bearer auth). This surface streams immediately and echoes the
45
+ * real model key — the legacy `/algo` chat endpoint answers
46
+ * `10605 isQueued` (parking requests in a server-side queue) and echoes a
47
+ * fixed `"auto"` model. Dispatch tries this surface first, and falls back to
48
+ * legacy COSY only when it refuses the account on entitlement grounds (HTTP
49
+ * 402 / `{"code":116}`) AND the dispatched model is a free-tier row — for a
50
+ * billed model the same code is genuine quota exhaustion and must surface.
51
+ */
52
+ export const QODER_IDE_CHAT_PATH = "/model/v1/chat/completions";
53
+
54
+ /** The model-server host for one realm (intl: `api2-v2`, cn: gateway). */
55
+ export function qoderIdeChatUrl(realm: QoderRealm): string {
56
+ return realm === "cn"
57
+ ? `https://gateway.qoder.com.cn${QODER_IDE_CHAT_PATH}`
58
+ : `https://api2-v2.qoder.sh${QODER_IDE_CHAT_PATH}`;
59
+ }
60
+
42
61
  /**
43
62
  * Realm → deployment.
44
63
  *
@@ -61,7 +80,9 @@ export const QODER_REALMS: Readonly<Record<QoderRealm, QoderRealmConfig>> = {
61
80
  gatewayFallbacks: [],
62
81
  openapi: "https://openapi.qoder.com.cn",
63
82
  website: "https://qoder.com.cn",
64
- clientId: "1c5e33e1-364d-4ce6-b02c-acaa81274a5c",
83
+ // 1:1 with the desktop IDE's device flow (`Hfl` in the unpacked worker):
84
+ // the client id the IDE registers for the CN deployment.
85
+ clientId: "e93fe488-5778-4c35-a6fc-0f54ed7b3139",
65
86
  redirectUri: "qoder-work-cn://",
66
87
  userAgent: "QoderWork/1.1.64",
67
88
  },
@@ -97,30 +118,3 @@ export function qoderInferenceCandidates(
97
118
  }
98
119
  return [config.gateway, ...config.gatewayFallbacks];
99
120
  }
100
-
101
- /**
102
- * Model keys that exist in exactly one realm.
103
- *
104
- * From the donor's `INTL_EXCLUSIVE` / `CN_EXCLUSIVE` tables. A request for one
105
- * of these on the wrong realm does not degrade — it is refused — so the roster
106
- * each realm publishes is the authority, not a union.
107
- */
108
- export const QODER_INTL_EXCLUSIVE: ReadonlySet<string> = new Set([
109
- "fast",
110
- "efficient",
111
- "cmodel",
112
- "smodel",
113
- "ultimate",
114
- "performance",
115
- ]);
116
-
117
- export const QODER_CN_EXCLUSIVE: ReadonlySet<string> = new Set([
118
- "q37fmodel",
119
- "gm51model",
120
- ]);
121
-
122
- /** True when this model key is served only by the other realm. */
123
- export function isForeignQoderModel(realm: QoderRealm, modelId: string): boolean {
124
- const foreign = realm === "cn" ? QODER_INTL_EXCLUSIVE : QODER_CN_EXCLUSIVE;
125
- return foreign.has(modelId.trim().toLowerCase());
126
- }
@@ -0,0 +1,159 @@
1
+ /**
2
+ * Qoder-specific SSE decoding.
3
+ *
4
+ * The generic `decodeSseEvents` splits on newlines and treats a bare line
5
+ * (no `data:` prefix) with no open event as malformed. Qoder's IDE surface
6
+ * embeds literal newline bytes inside JSON string values (e.g. `raw_usage`
7
+ * keys like `"data_policy_preferre\n\nnd_count"`), which fragments one logical
8
+ * `data:` event into many physical lines — including blank lines that are not
9
+ * event boundaries. The generic decoder can throw `Malformed SSE line` on the
10
+ * trailing fragments of those giant events.
11
+ *
12
+ * This decoder is tolerant by construction: it accumulates one event's payload
13
+ * across `data:` lines, bare continuations, and blank lines until the payload
14
+ * is a complete JSON value or the `[DONE]` sentinel. It never throws on a
15
+ * bare fragment — payload completeness is the only way an event ends.
16
+ */
17
+
18
+ export interface QoderSseEvent {
19
+ readonly event: string | null;
20
+ readonly data: string;
21
+ }
22
+
23
+ const DEFAULT_MAX_EVENT_BYTES = 8 * 1024 * 1024;
24
+
25
+ /** True when the accumulated text is a complete JSON value (or non-JSON text). */
26
+ function isCompleteJsonValue(data: string): boolean {
27
+ if (data.length === 0) return true;
28
+ let depth = 0;
29
+ let inString = false;
30
+ let escaped = false;
31
+ for (let i = 0; i < data.length; i += 1) {
32
+ const ch = data[i]!;
33
+ if (inString) {
34
+ if (escaped) {
35
+ escaped = false;
36
+ } else if (ch === "\\") {
37
+ escaped = true;
38
+ } else if (ch === '"') {
39
+ inString = false;
40
+ }
41
+ continue;
42
+ }
43
+ if (ch === '"') {
44
+ inString = true;
45
+ } else if (ch === "{" || ch === "[") {
46
+ depth += 1;
47
+ } else if (ch === "}" || ch === "]") {
48
+ depth -= 1;
49
+ if (depth < 0) return true;
50
+ }
51
+ }
52
+ return depth <= 0;
53
+ }
54
+
55
+ /**
56
+ * Decodes Qoder SSE into complete events.
57
+ */
58
+ export async function* decodeQoderSseEvents(
59
+ body: ReadableStream<Uint8Array> | null,
60
+ signal?: AbortSignal,
61
+ maxEventBytes = DEFAULT_MAX_EVENT_BYTES,
62
+ ): AsyncIterable<QoderSseEvent> {
63
+ if (body === null) return;
64
+
65
+ const reader = body.getReader();
66
+ const decoder = new TextDecoder();
67
+ let lineBuffer = "";
68
+ let current: string | null = null; // accumulated payload of the open event
69
+ let eventBytes = 0;
70
+
71
+ /** Returns the open payload when complete, else null (clearing it). */
72
+ const takeComplete = (): string | null => {
73
+ if (current === null) return null;
74
+ const trimmed = current.trim();
75
+ if (trimmed.length === 0) {
76
+ current = null;
77
+ eventBytes = 0;
78
+ return null;
79
+ }
80
+ if (trimmed === "[DONE]" || isCompleteJsonValue(trimmed)) {
81
+ const data = current;
82
+ current = null;
83
+ eventBytes = 0;
84
+ return data;
85
+ }
86
+ return null;
87
+ };
88
+
89
+ const appendPayload = (text: string): void => {
90
+ if (current === null) return;
91
+ current += text;
92
+ eventBytes += text.length;
93
+ if (eventBytes > maxEventBytes) throw new Error("SSE event exceeds size bound");
94
+ };
95
+
96
+ /** Emit a complete open payload, if any. Returns `true` when the stream ends. */
97
+ function* emitComplete(): Generator<QoderSseEvent, boolean, void> {
98
+ const data = takeComplete();
99
+ if (data === null) return false;
100
+ if (data.trim() === "[DONE]") return true;
101
+ yield { event: null, data };
102
+ return false;
103
+ }
104
+
105
+ try {
106
+ for (;;) {
107
+ if (signal?.aborted) return;
108
+ const { done, value } = await reader.read();
109
+ if (done) break;
110
+ if (!value || value.byteLength === 0) continue;
111
+ lineBuffer += decoder.decode(value, { stream: true });
112
+
113
+ for (;;) {
114
+ const nl = lineBuffer.indexOf("\n");
115
+ if (nl < 0) break;
116
+ const line = lineBuffer.slice(0, nl);
117
+ lineBuffer = lineBuffer.slice(nl + 1);
118
+ const trimmed = line.trim();
119
+
120
+ // Comments, event names, and other fields carry no payload.
121
+ if (trimmed.startsWith(":") || trimmed.startsWith("event:")) continue;
122
+
123
+ if (trimmed.startsWith("data:")) {
124
+ // A fresh `data:` — flush a complete open event first, then start a
125
+ // new payload with the slice after the prefix.
126
+ if (yield* emitComplete()) return;
127
+ current = line.slice(5).trimStart();
128
+ eventBytes = current.length;
129
+ if (eventBytes > maxEventBytes) throw new Error("SSE event exceeds size bound");
130
+ continue;
131
+ }
132
+
133
+ // Other known fields (id:, retry:, vendor extensions) carry no payload.
134
+ if (/^[a-zA-Z0-9_-]+:/.test(trimmed)) continue;
135
+
136
+ if (trimmed.length === 0) {
137
+ // Blank line: an event boundary only when the open payload is
138
+ // complete; otherwise it is a literal newline inside the JSON string.
139
+ if (yield* emitComplete()) return;
140
+ if (current !== null) appendPayload("\n");
141
+ continue;
142
+ }
143
+
144
+ // Bare line: continuation of the open payload (Qoder fragments JSON
145
+ // across physical lines with literal newlines inside strings).
146
+ if (current !== null) {
147
+ appendPayload("\n" + line);
148
+ if (yield* emitComplete()) return;
149
+ continue;
150
+ }
151
+ // Bare line with no open payload: ignore (tolerant by design).
152
+ }
153
+ }
154
+
155
+ yield* emitComplete();
156
+ } finally {
157
+ reader.releaseLock();
158
+ }
159
+ }