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
@@ -1,4 +1,4 @@
1
- import { Elysia, t } from "elysia";
1
+ import { Elysia, t, type AnyElysia } from "elysia";
2
2
  import { ConsoleDomainError, errorResponse, requireTenantScope } from "../../shared/errors";
3
3
  import { literalUnion } from "../../shared/elysia-schema";
4
4
  import type { AccessDecision } from "../../../security/access-control";
@@ -30,7 +30,7 @@ const ROUTING_BOUNDS = {
30
30
  maxInflightPerProxy: PROVIDER_ROUTING_MAX_INFLIGHT_BOUNDS,
31
31
  } as const;
32
32
 
33
- export function createProviderDetailOperations(deps: ProviderDetailConfig) {
33
+ function createProviderDetailOperations(deps: ProviderDetailConfig) {
34
34
  const operations = {
35
35
  async getRouting(
36
36
  access: AccessDecision | undefined,
@@ -160,7 +160,7 @@ const updateRoutingBody = t.Object({
160
160
  });
161
161
 
162
162
 
163
- export function createProviderDetailRoutes(config: ProviderDetailConfig): Elysia {
163
+ export function createProviderDetailRoutes(config: ProviderDetailConfig): AnyElysia {
164
164
  const factory = createProviderDetailOperations(config);
165
165
  return new Elysia({ prefix: "/providers" })
166
166
  .get("/:providerId/routing", async ({ request, params, set }) => {
@@ -191,5 +191,5 @@ export function createProviderDetailRoutes(config: ProviderDetailConfig): Elysia
191
191
  return providerDetailErrorResponse(e, set);
192
192
  }
193
193
  },
194
- ) as unknown as Elysia;
194
+ )
195
195
  }
@@ -28,7 +28,7 @@ import { log } from "../../../observability/logger";
28
28
  import { ConsoleDomainError } from "../../shared/errors";
29
29
 
30
30
  /** A loopback endpoint a callback can actually be delivered to. */
31
- export interface LoopbackCallbackEndpoint {
31
+ interface LoopbackCallbackEndpoint {
32
32
  readonly port: number;
33
33
  readonly path: string;
34
34
  }
@@ -44,7 +44,7 @@ const LOOPBACK_HOSTS = new Set(["127.0.0.1", "localhost", "::1", "[::1]"]);
44
44
  * installed app and a remote host is delivered over the network; neither is
45
45
  * something this process can bind, and both keep the manual path.
46
46
  */
47
- export function loopbackCallbackEndpoint(redirectUri: string): LoopbackCallbackEndpoint | undefined {
47
+ function loopbackCallbackEndpoint(redirectUri: string): LoopbackCallbackEndpoint | undefined {
48
48
  let url: URL;
49
49
  try {
50
50
  url = new URL(redirectUri);
@@ -59,12 +59,19 @@ export function loopbackCallbackEndpoint(redirectUri: string): LoopbackCallbackE
59
59
  }
60
60
 
61
61
  /** Completes one callback once its code and state have been read. */
62
- export interface OAuthCallbackCompleter {
62
+ interface OAuthCallbackCompleter {
63
63
  complete(
64
64
  providerId: string,
65
65
  code: string,
66
66
  state: string,
67
- ): Promise<{ readonly ok: boolean; readonly message: string }>;
67
+ /** Extra query params the callback carried (e.g. Kiro's `login_option`). */
68
+ callbackParams?: Readonly<Record<string, string>>,
69
+ ): Promise<{
70
+ readonly ok: boolean;
71
+ readonly message: string;
72
+ readonly redirectUrl?: string;
73
+ readonly continueState?: string;
74
+ }>;
68
75
  }
69
76
 
70
77
  /** Minimal `Bun.serve` surface this module depends on. */
@@ -79,7 +86,7 @@ type ServeFn = (options: {
79
86
  readonly fetch: (request: Request) => Response | Promise<Response>;
80
87
  }) => LoopbackServer;
81
88
 
82
- export interface CallbackListenerOptions {
89
+ interface CallbackListenerOptions {
83
90
  readonly completer: OAuthCallbackCompleter;
84
91
  /** Injected in tests; defaults to `Bun.serve`. */
85
92
  readonly serve?: ServeFn;
@@ -92,7 +99,10 @@ const DEFAULT_TTL_MS = 900_000;
92
99
 
93
100
  interface RegisteredFlow {
94
101
  readonly providerId: string;
102
+ /** Paths this flow's callback may arrive on (e.g. Kiro serves both `/oauth/callback` and `/signin/callback`). */
103
+ readonly paths: readonly string[];
95
104
  readonly timer: ReturnType<typeof setTimeout>;
105
+ inFlight: boolean;
96
106
  }
97
107
 
98
108
  interface PortListener {
@@ -134,17 +144,30 @@ export class OAuthCallbackListener {
134
144
  if (endpoint === undefined) return false;
135
145
  const existing = this.#ports.get(endpoint.port);
136
146
  if (existing !== undefined) {
137
- this.#remember(existing, state, providerId);
147
+ this.#remember(existing, state, providerId, this.#callbackPaths(endpoint.path));
138
148
  return true;
139
149
  }
140
150
  const servers = this.#bind(endpoint);
141
151
  const listener: PortListener = { port: endpoint.port, servers, flows: new Map() };
142
152
  this.#ports.set(endpoint.port, listener);
143
- this.#remember(listener, state, providerId);
153
+ this.#remember(listener, state, providerId, this.#callbackPaths(endpoint.path));
144
154
  log.info(`[oauth] callback listener bound on 127.0.0.1:${endpoint.port}${endpoint.path}`);
145
155
  return true;
146
156
  }
147
157
 
158
+ /**
159
+ * The paths one login may deliver its callback on. Kiro's portal is observed
160
+ * (unpacked IDE 1.4.62) to redirect to both `/oauth/callback` and
161
+ * `/signin/callback`; binding only the advertised path left a redirect to the
162
+ * other one hitting a 404 and the login hanging. Other providers neither
163
+ * register nor expect `/signin/callback`, so adding it is harmless.
164
+ */
165
+ #callbackPaths(advertised: string): readonly string[] {
166
+ return advertised === "/signin/callback"
167
+ ? [advertised]
168
+ : [advertised, "/signin/callback"];
169
+ }
170
+
148
171
  /** Drops a flow and frees its port when nothing else is waiting on it. */
149
172
  release(redirectUri: string, state: string): void {
150
173
  const endpoint = loopbackCallbackEndpoint(redirectUri);
@@ -154,6 +177,25 @@ export class OAuthCallbackListener {
154
177
  this.#forget(endpoint.port, listener, state);
155
178
  }
156
179
 
180
+ /**
181
+ * Re-registers an existing flow state under a new callback state.
182
+ *
183
+ * Kiro's two-phase portal login starts a second authorization leg with a
184
+ * *fresh* state (the IDE mints a new UUID for the IdC authorize step), so
185
+ * the loopback listener must retain the pending flow under that second
186
+ * state too — otherwise the phase-2 callback answers "OAuth session not
187
+ * found or expired" even though the flow is still alive.
188
+ */
189
+ track(redirectUri: string, state: string, newState: string): void {
190
+ const endpoint = loopbackCallbackEndpoint(redirectUri);
191
+ if (endpoint === undefined) return;
192
+ const listener = this.#ports.get(endpoint.port);
193
+ if (listener === undefined) return;
194
+ const flow = listener.flows.get(state);
195
+ if (flow === undefined) return;
196
+ this.#remember(listener, newState, flow.providerId, flow.paths);
197
+ }
198
+
157
199
  /**
158
200
  * Drops every flow for `providerId` on `redirectUri`'s port.
159
201
  *
@@ -182,7 +224,7 @@ export class OAuthCallbackListener {
182
224
  }
183
225
  }
184
226
 
185
- #remember(listener: PortListener, state: string, providerId: string): void {
227
+ #remember(listener: PortListener, state: string, providerId: string, paths: readonly string[]): void {
186
228
  const previous = listener.flows.get(state);
187
229
  if (previous !== undefined) clearTimeout(previous.timer);
188
230
  const timer = setTimeout(() => {
@@ -190,7 +232,7 @@ export class OAuthCallbackListener {
190
232
  }, this.#ttlMs);
191
233
  // A pending login must not hold the process open on its own.
192
234
  (timer as unknown as { unref?: () => void }).unref?.();
193
- listener.flows.set(state, { providerId, timer });
235
+ listener.flows.set(state, { providerId, paths, timer, inFlight: false });
194
236
  }
195
237
 
196
238
  #forget(port: number, listener: PortListener, state: string): void {
@@ -250,9 +292,6 @@ export class OAuthCallbackListener {
250
292
  endpoint: LoopbackCallbackEndpoint,
251
293
  ): Promise<Response> {
252
294
  const url = new URL(request.url);
253
- if (url.pathname !== endpoint.path) {
254
- return new Response("Not found", { status: 404 });
255
- }
256
295
  const listener = this.#ports.get(endpoint.port);
257
296
  if (listener === undefined) {
258
297
  return new Response("OAuth session not found or expired.", { status: 400 });
@@ -278,7 +317,7 @@ export class OAuthCallbackListener {
278
317
  { status: 400, headers: { "content-type": "text/html; charset=utf-8" } },
279
318
  );
280
319
  }
281
- if (code.length === 0) {
320
+ if (code.length === 0 && !url.searchParams.has("login_option")) {
282
321
  return new Response("Missing authorization code.", { status: 400 });
283
322
  }
284
323
  // `state` is the correlation key and the replay guard. An authorization
@@ -297,14 +336,45 @@ export class OAuthCallbackListener {
297
336
  if (matched === undefined) {
298
337
  return new Response("OAuth session not found or expired.", { status: 400 });
299
338
  }
300
- // Claim the flow before awaiting: a browser that retries the redirect must
301
- // not start a second exchange with a code that is already being spent.
339
+ // Some providers (Kiro's portal) can land the callback on any of several
340
+ // registered paths — `/oauth/callback` or `/signin/callback`. Accept the
341
+ // path the flow registered for, not only the advertised default.
342
+ if (!matched.paths.includes(url.pathname)) {
343
+ return new Response("Not found", { status: 404 });
344
+ }
345
+ if (matched.inFlight) {
346
+ return new Response("OAuth callback is already being processed.", { status: 409 });
347
+ }
348
+ // Claim while the exchange is in flight. A multi-step flow (Kiro OIDC) may
349
+ // return a redirect and reuse this listener/state for its next callback.
350
+ matched.inFlight = true;
302
351
  const matchedState = state.length > 0 ? state : this.#stateOf(listener, matched);
303
- if (matchedState !== undefined) this.#forget(endpoint.port, listener, matchedState);
304
- let result: { ok: boolean; message: string };
352
+ let result: {
353
+ ok: boolean;
354
+ message: string;
355
+ redirectUrl?: string;
356
+ continueState?: string;
357
+ };
358
+ // Some authorization servers (Kiro's portal) append provider-specific query
359
+ // params — `login_option` names the social/enterprise family that approved.
360
+ // The exchange needs them to rebuild the exact redirect URI the portal saw,
361
+ // and the OIDC families carry `issuer_url`/`idc_region`/`client_id`/`scopes`
362
+ // that the continuation flow needs. Forward every param other than the ones
363
+ // this handler already consumed.
364
+ const callbackParams: Record<string, string> = {};
365
+ for (const [key, value] of url.searchParams) {
366
+ if (key === "code" || key === "state" || key === "error" || key === "error_description") continue;
367
+ callbackParams[key] = value;
368
+ }
305
369
  try {
306
- result = await this.#completer.complete(matched.providerId, code, state);
370
+ result = await this.#completer.complete(
371
+ matched.providerId,
372
+ code,
373
+ state,
374
+ Object.keys(callbackParams).length > 0 ? callbackParams : undefined,
375
+ );
307
376
  } catch (failure) {
377
+ if (matchedState !== undefined) this.#forget(endpoint.port, listener, matchedState);
308
378
  const message = failure instanceof Error ? failure.message : "OAuth login failed";
309
379
  log.error(`[oauth] callback completion failed for ${matched.providerId}: ${message}`);
310
380
  return new Response(
@@ -312,6 +382,18 @@ export class OAuthCallbackListener {
312
382
  { status: 500, headers: { "content-type": "text/html; charset=utf-8" } },
313
383
  );
314
384
  }
385
+ if (result.redirectUrl !== undefined) {
386
+ // Phase 1 of a two-phase login (Kiro OIDC): the flow is still pending, so
387
+ // continue by sending the browser to the phase-2 authorize URL. The phase-2
388
+ // authorize URL carries a fresh state (`continueState`), which the completer
389
+ // already registered under — keep the current matcher alive for it.
390
+ matched.inFlight = false;
391
+ return new Response(null, {
392
+ status: 302,
393
+ headers: { location: result.redirectUrl },
394
+ });
395
+ }
396
+ if (matchedState !== undefined) this.#forget(endpoint.port, listener, matchedState);
315
397
  if (!result.ok) {
316
398
  return new Response(
317
399
  `<!doctype html><html><body><p>OAuth login failed: ${escapeHtml(result.message)}</p></body></html>`,
@@ -9,7 +9,7 @@ import type { ProviderRegistry } from "../../../providers/provider-registry";
9
9
  import { browserAuthorizeRedirectUri } from "../../../config/providers";
10
10
  import { log } from "../../../observability/logger";
11
11
  import { GatewayError } from "../../../protocol/gateway-error";
12
- import { Elysia, t } from "elysia";
12
+ import { Elysia, t, type AnyElysia } from "elysia";
13
13
  import { ConsoleDomainError, errorResponse, requireScope } from "../../shared/errors";
14
14
  import { generateOAuthState, generatePkcePair } from "../../../providers/authentication/oauth-flow-store";
15
15
  import type { OAuthFlowStorage } from "../../../providers/authentication/oauth-flow-store";
@@ -171,7 +171,7 @@ export class DrizzleOAuthAccountStore implements OAuthAccountStore {
171
171
  * server the redirect arrives at; `callback-listener.ts` is what makes those
172
172
  * the same endpoint.
173
173
  */
174
- export interface PersistedOAuthAccount {
174
+ interface PersistedOAuthAccount {
175
175
  readonly accountId: string;
176
176
  }
177
177
 
@@ -212,9 +212,20 @@ async function requireClient(
212
212
  }
213
213
 
214
214
  /** The result of exchanging one authorization code, as the caller should report it. */
215
- export interface OAuthCompleteOutcome {
215
+ interface OAuthCompleteOutcome {
216
216
  readonly ok: boolean;
217
217
  readonly message: string;
218
+ /**
219
+ * When set, the exchange is not finished: the browser must be redirected to
220
+ * this URL to continue (Kiro's OIDC families need a second authorization
221
+ * leg after the portal callback).
222
+ */
223
+ readonly redirectUrl?: string;
224
+ /**
225
+ * New callback state the phase-2 authorize URL carries (Kiro mints a fresh
226
+ * state for the IdC leg; the console retains the pending flow under it).
227
+ */
228
+ readonly continueState?: string;
218
229
  }
219
230
 
220
231
  /**
@@ -231,29 +242,41 @@ export async function completeLogin(
231
242
  providerId: string,
232
243
  code: string,
233
244
  state: string,
245
+ callbackParams?: Readonly<Record<string, string>>,
234
246
  ): Promise<OAuthCompleteOutcome> {
235
247
  // An empty `state` means the authorization server does not echo one
236
248
  // (OpenRouter omits it from the callback entirely); correlate by provider in
237
249
  // that case, and by state otherwise so the stronger check stays in force
238
250
  // wherever the server supports it.
251
+ //
252
+ // The flow is peeked, not consumed, until the exchange succeeds: consuming
253
+ // first means a failed exchange (a transient upstream blip, a paste that
254
+ // missed a parameter) burns the state and every retry answers "unknown or
255
+ // expired state". The state is deleted only after the account persists.
239
256
  const flow = state
240
- ? await config.oauthFlowStore.consumePending(state)
257
+ ? await config.oauthFlowStore.peekPending(state)
241
258
  : await config.oauthFlowStore.consumePendingByProvider(providerId);
242
259
  if (!flow || flow.providerId !== providerId) {
243
260
  return { ok: false, message: "unknown or expired state" };
244
261
  }
245
- // The flow is spent, so its listener registration has nothing left to wait
246
- // for; releasing it here keeps a manual paste from leaving the port bound.
247
- // Stateless callbacks arrive with `""`, which matches no filed state —
248
- // release by provider so the OpenRouter port does not linger until TTL.
249
- if (state.length > 0) config.callbackListener.release(flow.redirectUri, state);
250
- else config.callbackListener.releaseProvider(flow.redirectUri, providerId);
262
+ // Release the listener registration for this flow. The pending state itself
263
+ // stays available so a manual re-paste can retry the exchange. The release is
264
+ // deferred until after the exchange for two-phase flows: Kiro's OIDC portal
265
+ // callback (phase 1) must keep the loopback listener alive so the second
266
+ // authorization leg can land on it.
251
267
  const client = await requireClient(config.providerRegistry, providerId);
252
268
  if (!client.exchangeCode) {
253
269
  return { ok: false, message: "provider does not support browser authorization" };
254
270
  }
271
+ const releaseListener = () => {
272
+ if (state.length > 0) config.callbackListener.release(flow.redirectUri, state);
273
+ else config.callbackListener.releaseProvider(flow.redirectUri, providerId);
274
+ };
255
275
  try {
256
- const result = await client.exchangeCode(code, flow.codeVerifier, flow.redirectUri, state);
276
+ const result = await client.exchangeCode(code, flow.codeVerifier, flow.redirectUri, state, {
277
+ ...(callbackParams === undefined ? {} : { parameters: callbackParams }),
278
+ ...(flow.providerState === undefined ? {} : { providerState: flow.providerState }),
279
+ });
257
280
  const tokenCheck = await validateIssuedAccessToken(
258
281
  result.access,
259
282
  providerJwtVerification(providerId),
@@ -270,9 +293,51 @@ export async function completeLogin(
270
293
  label: result.accountLabel ?? flow.accountLabel,
271
294
  ...result,
272
295
  });
296
+ // The exchange succeeded and the account is persisted; now the flow is
297
+ // spent. Consuming it here (peeked above) frees the state and the provider
298
+ // pointer so a replayed callback cannot re-create the account.
299
+ releaseListener();
300
+ if (state.length > 0) await config.oauthFlowStore.consumePending(state);
301
+ else await config.oauthFlowStore.consumePendingByProvider(providerId);
273
302
  await config.snapshotInvalidator?.invalidate();
274
303
  return { ok: true, message: `${providerId} account connected. You may close this tab.` };
275
304
  } catch (error) {
305
+ // Kiro's OIDC families: the portal callback carries no code — only
306
+ // `login_option` + issuer/region. The client responded with the second-leg
307
+ // authorize URL plus a fresh PKCE verifier and the OIDC registration.
308
+ // Rewrite the pending flow so phase 2 exchanges against that verifier, keep
309
+ // the listener alive, and tell the caller to redirect the browser there.
310
+ // dynamic-import: B-02 lazy graph — 58KB kiro OIDC path, only for Kiro flows.
311
+ // The kiro-oauth graph is 58KB, so it is imported lazily here — only the
312
+ // Kiro OIDC path needs the error class — instead of on the console boot
313
+ // graph (B-02).
314
+ const { KiroOidcPendingError } = await import(
315
+ "../../../providers/integrations/kiro/kiro-oauth"
316
+ );
317
+ if (error instanceof KiroOidcPendingError) {
318
+ // Phase 1 of Kiro's OIDC portal login: the portal callback carried no
319
+ // code — only `login_option` + issuer/region. The client answered with the
320
+ // phase-2 authorize URL, a fresh PKCE verifier, and the OIDC client
321
+ // registration. Mirror the 1.4.62 IDE: the second leg uses a *new* state
322
+ // (not the portal's), so mint it here, keep the pending flow reachable
323
+ // under both states, and tell the caller which state to expect back.
324
+ const continueState = error.continueState;
325
+ if (state.length > 0 && continueState.length > 0) {
326
+ await config.oauthFlowStore.savePending(continueState, {
327
+ ...flow,
328
+ codeVerifier: error.codeVerifier,
329
+ providerState: error.providerState,
330
+ });
331
+ config.callbackListener.track(flow.redirectUri, state, continueState);
332
+ }
333
+ return {
334
+ ok: false,
335
+ message: error.message,
336
+ redirectUrl: error.authorizeUrl,
337
+ continueState,
338
+ };
339
+ }
340
+ releaseListener();
276
341
  // Upstream token-endpoint bodies can echo credentials or sensitive
277
342
  // diagnostics — the browser gets a generic failure; the bounded detail
278
343
  // stays server-side in the console log.
@@ -351,7 +416,7 @@ function failurePage(message: string, wantsJson: boolean): Response {
351
416
  );
352
417
  }
353
418
 
354
- export function createOAuthLoginOperations(config: OAuthLoginConfig) {
419
+ function createOAuthLoginOperations(config: OAuthLoginConfig) {
355
420
  const operations = {
356
421
  async beginAuthorize(
357
422
  access: AccessDecision | undefined,
@@ -437,8 +502,15 @@ export function createOAuthLoginOperations(config: OAuthLoginConfig) {
437
502
  code: string,
438
503
  state: string,
439
504
  wantsJson = false,
505
+ callbackParams?: Readonly<Record<string, string>>,
440
506
  ): Promise<Response> {
441
- const outcome = await completeLogin(config, providerId, code, state);
507
+ const outcome = await completeLogin(config, providerId, code, state, callbackParams);
508
+ if (outcome.redirectUrl !== undefined) {
509
+ return new Response(null, {
510
+ status: 302,
511
+ headers: { location: outcome.redirectUrl },
512
+ });
513
+ }
442
514
  return outcome.ok ? successPage(providerId, wantsJson) : failurePage(outcome.message, wantsJson);
443
515
  },
444
516
  async handleCallbackError(
@@ -639,7 +711,7 @@ const importBody = t.Object({
639
711
  accountLabel: t.Optional(t.String()),
640
712
  });
641
713
 
642
- export function createOAuthLoginRoutes(config: OAuthLoginConfig): Elysia {
714
+ export function createOAuthLoginRoutes(config: OAuthLoginConfig): AnyElysia {
643
715
  const factory = createOAuthLoginOperations(config);
644
716
  return new Elysia({ prefix: "/providers" })
645
717
  .post(
@@ -670,7 +742,14 @@ export function createOAuthLoginRoutes(config: OAuthLoginConfig): Elysia {
670
742
  // `state` is absent for authorization servers that do not echo it
671
743
  // (OpenRouter). The flow is then correlated by provider instead — one
672
744
  // browser login is in flight per provider at a time.
673
- return factory.handleCallback(params.providerId, code, state, wantsJson);
745
+ const loginOption = typeof query["login_option"] === "string" ? query["login_option"] : undefined;
746
+ return factory.handleCallback(
747
+ params.providerId,
748
+ code,
749
+ state,
750
+ wantsJson,
751
+ loginOption === undefined ? undefined : { login_option: loginOption },
752
+ );
674
753
  })
675
754
  .post(
676
755
  "/:providerId/oauth/device/start",
@@ -721,6 +800,6 @@ export function createOAuthLoginRoutes(config: OAuthLoginConfig): Elysia {
721
800
  return oauthErrorResponse(e, set);
722
801
  }
723
802
  },
724
- ) as unknown as Elysia;
803
+ )
725
804
  }
726
805
 
@@ -1,10 +1,17 @@
1
1
  import { t } from "elysia";
2
2
  import type { Elysia } from "elysia";
3
- import { and, eq, isNull } from "drizzle-orm";
3
+ import { and, eq, inArray, isNull } from "drizzle-orm";
4
4
  import { adminAuditLog, providerAccounts } from "../../persistence/schema";
5
5
  import { requireGlobalAdmin } from "../shared/errors";
6
- import { GLOBAL_QUOTA_LENS, invalidateQuotaCache } from "../../providers/quota/cache";
7
- import { invalidateCredentialCache } from "../../providers/operations/provider-credential-service";
6
+ import {
7
+ GLOBAL_QUOTA_LENS,
8
+ invalidateQuotaCache,
9
+ invalidateQuotaCaches,
10
+ } from "../../providers/quota/cache";
11
+ import {
12
+ invalidateCredentialCache,
13
+ invalidateCredentialCaches,
14
+ } from "../../providers/operations/provider-credential-service";
8
15
  import {
9
16
  QUOTA_REFRESH_TIMEOUT_MS,
10
17
  loadQuotaTarget,
@@ -119,6 +126,37 @@ export function registerAccountQuotaGlobalRoutes(
119
126
  });
120
127
  return { success: true };
121
128
  })
129
+ .post("/global/accounts/batch-delete", {
130
+ body: t.Object({
131
+ accountIds: t.Array(t.String()),
132
+ }),
133
+ }, async ({ request, body }) => {
134
+ const access = requireGlobalAdmin(accessResolver(request));
135
+ const accountIds = [...new Set(body.accountIds)];
136
+ if (accountIds.length === 0) {
137
+ return { ok: true, deleted: [], missing: [] };
138
+ }
139
+ // One DELETE statement scoped to shared accounts only: a tenant-owned row
140
+ // (`tenant_id IS NOT NULL`) is never matchable by a global admin, so it
141
+ // lands in `missing`.
142
+ const deleted = await db
143
+ .delete(providerAccounts)
144
+ .where(and(inArray(providerAccounts.id, accountIds), isNull(providerAccounts.tenantId)))
145
+ .returning({ id: providerAccounts.id });
146
+ const deletedIds = deleted.map((row) => row.id);
147
+ const deletedSet = new Set(deletedIds);
148
+ const missing = accountIds.filter((id) => !deletedSet.has(id));
149
+ invalidateCredentialCaches(deletedIds);
150
+ await invalidateQuotaCaches(GLOBAL_QUOTA_LENS, deletedIds, redis);
151
+ await snapshotInvalidator?.invalidate();
152
+ await db.insert(adminAuditLog).values({
153
+ actor: access.admissionIdentity,
154
+ action: "provider_account.global.batch_deleted",
155
+ target: "global",
156
+ detail: { deleted: deletedIds, missing },
157
+ });
158
+ return { ok: true, deleted: deletedIds, missing };
159
+ })
122
160
  .post("/global/accounts/:id/quota/refresh", async ({ request, params, set }) => {
123
161
  const access = requireGlobalAdmin(accessResolver(request));
124
162
  const target = await loadQuotaTarget(db, params.id, null);
@@ -1,6 +1,5 @@
1
1
  import type { ConsoleAccessResolver } from "../auth/access";
2
2
  import type { AuditRecorder } from "../auth/audit-recorder";
3
- import { markClineApiKeyCredential } from "../../providers/integrations/cline/cline-quota";
4
3
  import { type QuotaRefreshDeps } from "../../providers/quota/refresh";
5
4
  import type { AccountTestEgressDeps } from "./account-test-egress";
6
5
 
@@ -31,8 +30,16 @@ export type AccountStatus = (typeof ACCOUNT_STATUSES)[number];
31
30
  * omitted it, so a working account was cooled with `quota_fetch_failed` every
32
31
  * minute while refreshing fine from the console.
33
32
  */
34
- export function markProviderApiKeyCredential(providerId: string, credential: string): string {
35
- return providerId === "cline" ? markClineApiKeyCredential(credential) : credential;
33
+ export async function markProviderApiKeyCredential(providerId: string, credential: string): Promise<string> {
34
+ if (providerId !== "cline") return credential;
35
+ // dynamic-import: B-02 lazy graph — cline-only marker, kept off the boot graph.
36
+ // The cline-quota graph is loaded lazily: only `cline` accounts ever need
37
+ // the marker, so a deployment without cline must not pull it into the boot
38
+ // graph (B-02).
39
+ const { markClineApiKeyCredential } = await import(
40
+ "../../providers/integrations/cline/cline-quota"
41
+ );
42
+ return markClineApiKeyCredential(credential);
36
43
  }
37
44
 
38
45
  /**