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
@@ -36,7 +36,7 @@ export const RELAY_TARGETS: readonly RelayTarget[] = ["cloudflare", "vercel", "d
36
36
  * The same handler body runs on Workers and Vercel Edge (`export default {
37
37
  * fetch }`); the Deno entry wraps it in `Deno.serve`.
38
38
  */
39
- export const RELAY_WORKER_SOURCE = `// Cartethyia hosted relay.
39
+ const RELAY_WORKER_SOURCE = `// Cartethyia hosted relay.
40
40
  //
41
41
  // Forwards a request to the origin named by the x-relay-target header, at the
42
42
  // path named by x-relay-path. Deployed to Cloudflare Workers / Vercel Edge.
@@ -83,7 +83,7 @@ export default handler;
83
83
  `;
84
84
 
85
85
  /** A Deno Deploy entry wraps the shared handler in `Deno.serve`. */
86
- export const RELAY_DENO_SOURCE = `${RELAY_WORKER_SOURCE}
86
+ const RELAY_DENO_SOURCE = `${RELAY_WORKER_SOURCE}
87
87
  Deno.serve((request) => handler.fetch(request));
88
88
  `;
89
89
 
@@ -111,7 +111,7 @@ export interface RelayDeployResult {
111
111
  }
112
112
 
113
113
  /** Outbound fetch seam (the validated fetch), so tests can stub deploys. */
114
- export type RelayFetch = (url: string, init: RequestInit) => Promise<Response>;
114
+ type RelayFetch = (url: string, init: RequestInit) => Promise<Response>;
115
115
 
116
116
  function generateName(prefix: string): string {
117
117
  return `${prefix}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`;
@@ -151,7 +151,7 @@ async function deployCloudflare(
151
151
  request: RelayDeployRequest,
152
152
  ): Promise<RelayDeployResult> {
153
153
  const accountId = request.accountId?.trim();
154
- if (!accountId) throw new GatewayError("invalid_request", 400, "Cloudflare account id is required");
154
+ if (!accountId) throw new GatewayError("invalid_request", 400, "Cloudflare account id is required (request {requestId}). Check the shape of what you sent — then resend 🪞");
155
155
  const projectName = request.projectName?.trim() || generateName("relay");
156
156
  const scriptUrl = `https://api.cloudflare.com/client/v4/accounts/${encodeURIComponent(accountId)}/workers/scripts/${encodeURIComponent(projectName)}`;
157
157
  const form = new FormData();
@@ -174,7 +174,7 @@ async function deployCloudflare(
174
174
  body: form,
175
175
  });
176
176
  if (!upload.ok)
177
- throw new GatewayError("platform_unavailable", upload.status, `Cloudflare deploy failed: ${await readError(upload)}`);
177
+ throw new GatewayError("platform_unavailable", upload.status, `Cloudflare deploy failed (request {requestId}): ${await readError(upload)}. Flip the switch back on when ready 🔌`);
178
178
  // Enable the workers.dev route; a failure here is non-fatal because the
179
179
  // subdomain lookup below decides the final URL and reports its own error.
180
180
  await fetchImpl(`${scriptUrl}/subdomain`, {
@@ -190,7 +190,7 @@ async function deployCloudflare(
190
190
  throw new GatewayError(
191
191
  "platform_unavailable",
192
192
  subdomainRes.status,
193
- `Worker uploaded but its workers.dev subdomain could not be read: ${await readError(subdomainRes)}`,
193
+ `Worker uploaded but its workers.dev subdomain could not be read (request {requestId}): ${await readError(subdomainRes)}. Flip the switch back on when ready 🔌`,
194
194
  );
195
195
  const subdomainData = (await subdomainRes.json().catch(() => ({}))) as {
196
196
  result?: { subdomain?: string };
@@ -200,7 +200,7 @@ async function deployCloudflare(
200
200
  throw new GatewayError(
201
201
  "platform_unavailable",
202
202
  502,
203
- "Worker uploaded but no workers.dev subdomain is configured for this Cloudflare account.",
203
+ "Worker uploaded but no workers.dev subdomain is configured for this Cloudflare account (request {requestId}). Flip the switch back on when ready 🔌",
204
204
  );
205
205
  return { target: "cloudflare", projectName, relayUrl: `https://${projectName}.${subdomain}.workers.dev` };
206
206
  }
@@ -251,7 +251,7 @@ async function deployVercel(
251
251
  }),
252
252
  });
253
253
  if (!deployRes.ok)
254
- throw new GatewayError("platform_unavailable", deployRes.status, `Vercel deploy failed: ${await readError(deployRes)}`);
254
+ throw new GatewayError("platform_unavailable", deployRes.status, `Vercel deploy failed (request {requestId}): ${await readError(deployRes)}. Flip the switch back on when ready 🔌`);
255
255
  // Pin the region again after the deploy created the project: the PATCH above
256
256
  // is a no-op on first deploy (the project does not exist yet), and a redeploy
257
257
  // is what Vercel requires for a region change to take effect anyway. The SSO
@@ -280,7 +280,7 @@ async function deployVercel(
280
280
  }).catch(() => undefined);
281
281
  const deployment = (await deployRes.json().catch(() => ({}))) as { url?: string };
282
282
  if (!deployment.url)
283
- throw new GatewayError("platform_unavailable", 502, "Vercel deploy returned no URL");
283
+ throw new GatewayError("platform_unavailable", 502, "Vercel deploy returned no URL (request {requestId}). Flip the switch back on when ready 🔌");
284
284
  return {
285
285
  target: "vercel",
286
286
  projectName,
@@ -300,11 +300,11 @@ async function deployDeno(
300
300
  body: JSON.stringify({ project: projectName, entryPoint: "main.ts", files: { "main.ts": RELAY_DENO_SOURCE } }),
301
301
  });
302
302
  if (!deployRes.ok)
303
- throw new GatewayError("platform_unavailable", deployRes.status, `Deno deploy failed: ${await readError(deployRes)}`);
303
+ throw new GatewayError("platform_unavailable", deployRes.status, `Deno deploy failed (request {requestId}): ${await readError(deployRes)}. Flip the switch back on when ready 🔌`);
304
304
  const deployment = (await deployRes.json().catch(() => ({}))) as { url?: string; domains?: string[] };
305
305
  const url = deployment.url ?? deployment.domains?.[0];
306
306
  if (!url)
307
- throw new GatewayError("platform_unavailable", 502, "Deno deploy returned no URL");
307
+ throw new GatewayError("platform_unavailable", 502, "Deno deploy returned no URL (request {requestId}). Flip the switch back on when ready 🔌");
308
308
  return {
309
309
  target: "deno",
310
310
  projectName,
@@ -333,6 +333,6 @@ export async function deployRelay(
333
333
  request: RelayDeployRequest,
334
334
  ): Promise<RelayDeployResult> {
335
335
  if (!request.token.trim())
336
- throw new GatewayError("invalid_request", 400, `${request.target} API token is required`);
336
+ throw new GatewayError("invalid_request", 400, `${request.target} API token is required (request {requestId}). Wrong key, right door — check the credential 🔑`);
337
337
  return DEPLOYERS[request.target](fetchImpl, request);
338
338
  }
@@ -1,5 +1,5 @@
1
1
  // Network-pool control-plane HTTP routes. The only module that touches Elysia.
2
- import { Elysia, t } from "elysia";
2
+ import { Elysia, t, type AnyElysia } from "elysia";
3
3
  import { consoleErrorHandler } from "../../shared/errors";
4
4
  import { literalUnion } from "../../shared/elysia-schema";
5
5
  import { RELAY_TARGETS, type RelayDeployRequest } from "./relay-deploy";
@@ -56,7 +56,7 @@ const importPoolsBody = t.Object({
56
56
  family: t.Optional(t.Union([t.String(), t.Null()])),
57
57
  labelPrefix: t.Optional(t.String()),
58
58
  });
59
- export function createNetworkPoolRoutes(config: NetworkPoolConfig): Elysia {
59
+ export function createNetworkPoolRoutes(config: NetworkPoolConfig): AnyElysia {
60
60
  const factory = createNetworkPoolOperations(config);
61
61
  return new Elysia({ prefix: "/network/pools" })
62
62
  .error(consoleErrorHandler("Network pool operation failed"))
@@ -207,5 +207,5 @@ export function createNetworkPoolRoutes(config: NetworkPoolConfig): Elysia {
207
207
  body as Partial<PoolStrategySetting>,
208
208
  );
209
209
  },
210
- ) as unknown as Elysia;
210
+ );
211
211
  }
@@ -1,7 +1,9 @@
1
1
  // Drizzle-backed console persistence for network pools.
2
+ import { randomUUID } from "node:crypto";
2
3
  import { and, asc, eq, inArray, sql } from "drizzle-orm";
3
4
  import type { CartethyiaDatabase } from "../../../persistence/db-handle";
4
- import { networkPools, poolRoutingSettings } from "../../../persistence/schema";
5
+ import { networkPools, poolRoutingSettings, providerPoolAccounts } from "../../../persistence/schema";
6
+ import { providerRateLimitIsIpScoped } from "../../../providers/provider-metadata";
5
7
  import {
6
8
  DEFAULT_POOL_STRATEGY,
7
9
  type CreateNetworkPoolRequest,
@@ -444,6 +446,24 @@ export class DrizzleNetworkPoolStore implements NetworkPoolStore {
444
446
  ...(record.family !== undefined ? { family: record.family } : {}),
445
447
  }));
446
448
  await this.db.insert(networkPools).values(values);
449
+
450
+ // Materialize pool-as-account rows for ip-scoped, credential-less
451
+ // providers (OpenCode Free today): the operator sees the egress in the
452
+ // provider's Accounts card, and routing excludes a spent egress like a
453
+ // cooling account. Every other provider gets no row.
454
+ if (providerRateLimitIsIpScoped("opencode")) {
455
+ const now = new Date();
456
+ const poolAccounts = values.map((value) => ({
457
+ id: randomUUID(),
458
+ tenantId: value.tenantId,
459
+ networkPoolId: value.id,
460
+ providerId: "opencode",
461
+ label: `Pool ${value.id.slice(0, 8)}`,
462
+ createdAt: now,
463
+ updatedAt: now,
464
+ }));
465
+ await this.db.insert(providerPoolAccounts).values(poolAccounts);
466
+ }
447
467
  return values.length;
448
468
  }
449
469
 
@@ -53,6 +53,12 @@ export interface RuntimeSettingsResponse {
53
53
  readonly telemetryWindowMinutes: number | null;
54
54
  /** Per-tenant inline attachment ceiling in bytes; null means deployment default. */
55
55
  readonly imageClampMaxBytes: number | null;
56
+ /**
57
+ * Ordered vision-model ids used to describe images when the selected model
58
+ * is not image-capable. Empty means the legacy `[image]` placeholder is
59
+ * used instead.
60
+ */
61
+ readonly visionHandoffModels: readonly string[];
56
62
  readonly updatedAt: string;
57
63
  /*
58
64
  * What each `null` actually resolves to, in the same units the row stores.
@@ -67,7 +73,7 @@ export interface RuntimeSettingsResponse {
67
73
  }
68
74
 
69
75
  /** The deployment-level values a `null` runtime setting falls back to. */
70
- export interface RuntimeSettingsDefaults {
76
+ interface RuntimeSettingsDefaults {
71
77
  /** Seconds; from `CARTETHYIA_UPSTREAM_TIMEOUT_MS`. */
72
78
  readonly upstreamTimeoutSeconds: number;
73
79
  /** Minutes; what the payload store expires frames after. */
@@ -122,7 +128,7 @@ export type TelemetryPayloadMode = (typeof TELEMETRY_PAYLOAD_MODES)[number];
122
128
  export const TELEMETRY_PAYLOAD_DEPTHS = ["minimum", "moderate", "maximum"] as const;
123
129
  /** Capture depth for `full` mode accepted by the runtime-settings PATCH. */
124
130
  export type TelemetryPayloadDepth = (typeof TELEMETRY_PAYLOAD_DEPTHS)[number];
125
- export const PRIVACY_MODES = ["masked", "full"] as const;
131
+ const PRIVACY_MODES = ["masked", "full"] as const;
126
132
  /** Provider/model label privacy accepted by the runtime-settings PATCH. */
127
133
  export type PrivacyMode = (typeof PRIVACY_MODES)[number];
128
134
  /** PonyTail directive intensity accepted by the runtime-settings PATCH. */
@@ -130,6 +136,9 @@ export type PonyTailLevel = (typeof PONYTAIL_LEVELS)[number];
130
136
  /** RTK prune strength accepted by the runtime-settings PATCH. */
131
137
  export type RtkLevel = (typeof RTK_LEVELS)[number];
132
138
 
139
+ /** Upper bound on the number of ordered vision-handoff models. */
140
+ export const VISION_HANDOFF_MAX_MODELS = 8;
141
+
133
142
  export function createRuntimeSettingsOperations(deps: RuntimeSettingsConfig) {
134
143
  const operations = {
135
144
  async get(access: AccessDecision | undefined): Promise<RuntimeSettingsResponse> {
@@ -178,6 +187,23 @@ export function createRuntimeSettingsOperations(deps: RuntimeSettingsConfig) {
178
187
  if (patch.rtkPruneLevel !== undefined && !RTK_LEVELS.includes(patch.rtkPruneLevel)) {
179
188
  throw new ConsoleDomainError("invalid_request", 400, "Invalid rtkPruneLevel");
180
189
  }
190
+ if (patch.visionHandoffModels !== undefined) {
191
+ if (!Array.isArray(patch.visionHandoffModels)) {
192
+ throw new ConsoleDomainError("invalid_request", 400, "visionHandoffModels must be an array of model ids");
193
+ }
194
+ if (patch.visionHandoffModels.length > VISION_HANDOFF_MAX_MODELS) {
195
+ throw new ConsoleDomainError(
196
+ "invalid_request",
197
+ 400,
198
+ `visionHandoffModels may list at most ${VISION_HANDOFF_MAX_MODELS} models`,
199
+ );
200
+ }
201
+ for (const model of patch.visionHandoffModels) {
202
+ if (typeof model !== "string" || model.trim().length === 0) {
203
+ throw new ConsoleDomainError("invalid_request", 400, "visionHandoffModels entries must be non-empty model ids");
204
+ }
205
+ }
206
+ }
181
207
  const updated = await deps.store.update(a.tenantId, patch);
182
208
  await deps.auditSink?.record({
183
209
  access: a,
@@ -245,4 +271,5 @@ export const runtimeUpdateBody = t.Object({
245
271
  imageClampMaxBytes: t.Optional(
246
272
  t.Union([t.Null(), t.Number({ minimum: 64 * 1024, maximum: 100 * 1024 * 1024 })]),
247
273
  ),
274
+ visionHandoffModels: t.Optional(t.Array(t.String())),
248
275
  });
@@ -1,4 +1,4 @@
1
- import { Elysia } from "elysia";
1
+ import { Elysia, type AnyElysia } from "elysia";
2
2
  import {
3
3
  createRuntimeSettingsOperations,
4
4
  runtimeUpdateBody,
@@ -7,7 +7,7 @@ import {
7
7
  type UpdateRuntimeSettingsRequest,
8
8
  } from "./contracts";
9
9
 
10
- export function createRuntimeSettingsRoutes(config: RuntimeSettingsConfig): Elysia {
10
+ export function createRuntimeSettingsRoutes(config: RuntimeSettingsConfig): AnyElysia {
11
11
  const factory = createRuntimeSettingsOperations(config);
12
12
  return new Elysia({ prefix: "/settings/runtime" })
13
13
  .get("/", async ({ request, set }) => {
@@ -26,5 +26,5 @@ export function createRuntimeSettingsRoutes(config: RuntimeSettingsConfig): Elys
26
26
  } catch (e) {
27
27
  return runtimeSettingsErrorResponse(e, set);
28
28
  }
29
- }) as unknown as Elysia;
29
+ });
30
30
  }
@@ -15,6 +15,7 @@ import {
15
15
  RTK_LEVELS,
16
16
  TELEMETRY_PAYLOAD_DEPTHS,
17
17
  TELEMETRY_PAYLOAD_MODES,
18
+ VISION_HANDOFF_MAX_MODELS,
18
19
  type PonyTailLevel,
19
20
  type ResponsesReasoningSummary,
20
21
  type RtkLevel,
@@ -115,6 +116,22 @@ function normalizeConcurrencyTolerance(value: unknown): number | null {
115
116
  return normalizeBoundedNumber(value, 0, 1024);
116
117
  }
117
118
 
119
+ /** Ordered vision-handoff models: non-empty strings, trimmed, de-duplicated. */
120
+ function normalizeVisionHandoffModels(value: unknown): readonly string[] {
121
+ if (!Array.isArray(value)) return [];
122
+ const seen = new Set<string>();
123
+ const out: string[] = [];
124
+ for (const entry of value) {
125
+ if (typeof entry !== "string") continue;
126
+ const model = entry.trim();
127
+ if (model.length === 0 || seen.has(model)) continue;
128
+ seen.add(model);
129
+ out.push(model);
130
+ if (out.length >= VISION_HANDOFF_MAX_MODELS) break;
131
+ }
132
+ return out;
133
+ }
134
+
118
135
  function mapRuntimeSettingsRow(
119
136
  row: typeof consoleSettings.$inferSelect | undefined,
120
137
  redisBackend: RedisBackend,
@@ -148,6 +165,7 @@ function mapRuntimeSettingsRow(
148
165
  upstreamTimeoutSeconds: normalizeUpstreamTimeoutSeconds(prefs.upstreamTimeoutSeconds),
149
166
  telemetryWindowMinutes: normalizeTelemetryWindowMinutes(prefs.telemetryWindowMinutes),
150
167
  imageClampMaxBytes: normalizeImageClampMaxBytes(prefs.imageClampMaxBytes),
168
+ visionHandoffModels: normalizeVisionHandoffModels(prefs.visionHandoffModels),
151
169
  updatedAt,
152
170
  /*
153
171
  * Resolved at request time, not at module load: all four come from env the
@@ -211,6 +229,8 @@ export class DrizzleRuntimeSettingsStore implements RuntimeSettingsStore {
211
229
  );
212
230
  if (patch.imageClampMaxBytes !== undefined)
213
231
  patchPrefs.imageClampMaxBytes = normalizeImageClampMaxBytes(patch.imageClampMaxBytes);
232
+ if (patch.visionHandoffModels !== undefined)
233
+ patchPrefs.visionHandoffModels = normalizeVisionHandoffModels(patch.visionHandoffModels);
214
234
 
215
235
  const rows = await this.db
216
236
  .insert(consoleSettings)
@@ -4,7 +4,7 @@ import { globalOrOwnedBy } from "../../persistence/tenant-scope";
4
4
  // Share URLs reveal policy and allow one child key to be created per canonical
5
5
  // client IP. The child bearer is returned only by the successful issue call.
6
6
 
7
- import { Elysia } from "elysia";
7
+ import { Elysia, type AnyElysia } from "elysia";
8
8
  import { and, eq, sql } from "drizzle-orm";
9
9
  import type { CartethyiaDatabase } from "../../persistence/db-handle";
10
10
  import { apiKeys, models, providers } from "../../persistence/schema";
@@ -34,12 +34,83 @@ const MIN_TOKEN_LENGTH = 20;
34
34
  /**
35
35
  * Cadence of the share stats SSE snapshot.
36
36
  *
37
- * Raised with `SHARE_STATS_CACHE_TTL_SECONDS` (10 s). At 2 s the tick was
38
- * shorter than the rollup's own cache TTL, so every push re-parsed the whole
39
- * retained telemetry window — the interval was the thing making this read
40
- * expensive, not the number of viewers. Short enough to still read as live.
37
+ * Roughly one tenth of the cache TTL (`SHARE_STATS_CACHE_TTL_SECONDS`, 300
38
+ * s), so nearly every tick is served from the cache and the cold-compute
39
+ * path runs at most once per five minutes per family. Short enough to still
40
+ * read as live.
41
41
  */
42
- const SHARE_STATS_STREAM_INTERVAL_MS = 10_000;
42
+ const SHARE_STATS_STREAM_INTERVAL_MS = 30_000;
43
+
44
+ /**
45
+ * A subscriber of the process-wide share-stats sampler. A recipient is keyed
46
+ * by its family (`tenantId` + template key) so one sampler tick can push to
47
+ * many links without a per-connection timer.
48
+ */
49
+ type ShareStatsListener = (family: ShareFamilyStats) => void;
50
+
51
+ /**
52
+ * One sampler per gateway for share-stats SSE delivery. Previously each
53
+ * `/share/:token/stats/stream` connection ran its own 30s timer and re-read
54
+ * the family rollup; with N open tabs that is N timers and N rollups all
55
+ * reading the same data. Now a single process-level sampler ticks on the
56
+ * shared cadence and fans each computed rollup out to the subscribers whose
57
+ * family it matches. Timers run only while at least one stream is open.
58
+ */
59
+ function createShareStatsSampler(config: {
60
+ readonly db: CartethyiaDatabase;
61
+ readonly shareStore: ShareLinkStore;
62
+ readonly stats: ShareStatsPort;
63
+ }): { subscribe(token: string, listener: ShareStatsListener): () => void } {
64
+ const subscribers = new Map<string, Set<ShareStatsListener>>();
65
+ let timer: ReturnType<typeof setTimeout> | undefined;
66
+
67
+ const tick = async (): Promise<void> => {
68
+ // One resolve per family token: a recipient with several tabs shares one
69
+ // rollup, and a family with no open stream is never computed.
70
+ const tokens = new Set(subscribers.keys());
71
+ for (const token of tokens) {
72
+ const listeners = subscribers.get(token);
73
+ if (listeners === undefined || listeners.size === 0) continue;
74
+ try {
75
+ const family = await resolveFamilyStats(config.db, config.shareStore, config.stats, token);
76
+ if (family === null) continue;
77
+ for (const listener of listeners) listener(family);
78
+ } catch {
79
+ // A transient read must not interrupt the sampler.
80
+ }
81
+ }
82
+ };
83
+
84
+ const start = (): void => {
85
+ if (timer !== undefined) return;
86
+ timer = setTimeout(async function run() {
87
+ timer = setTimeout(run, SHARE_STATS_STREAM_INTERVAL_MS);
88
+ await tick();
89
+ }, SHARE_STATS_STREAM_INTERVAL_MS);
90
+ };
91
+
92
+ const stop = (): void => {
93
+ if (timer !== undefined) clearTimeout(timer);
94
+ timer = undefined;
95
+ };
96
+
97
+ return {
98
+ subscribe(token, listener) {
99
+ let listeners = subscribers.get(token);
100
+ if (listeners === undefined) {
101
+ listeners = new Set();
102
+ subscribers.set(token, listeners);
103
+ }
104
+ listeners.add(listener);
105
+ if (subscribers.size > 0) start();
106
+ return () => {
107
+ listeners.delete(listener);
108
+ if (listeners.size === 0) subscribers.delete(token);
109
+ if (subscribers.size === 0) stop();
110
+ };
111
+ },
112
+ };
113
+ }
43
114
 
44
115
  /**
45
116
  * Context and capabilities for one allowed model, sourced from the public
@@ -56,7 +127,7 @@ export interface ShareModelInfo {
56
127
  readonly webSearch: boolean;
57
128
  }
58
129
 
59
- export interface ShareRouterOptions {
130
+ interface ShareRouterOptions {
60
131
  readonly db: CartethyiaDatabase;
61
132
  readonly shareStore: ShareLinkStore;
62
133
  /** Resolves the normalized client IP through the trusted-proxy boundary. */
@@ -89,12 +160,12 @@ function json(body: unknown, status = 200): Response {
89
160
  function notFound(refusal: ShareLinkRefusal = "not_found"): Response {
90
161
  const message =
91
162
  refusal === "expired"
92
- ? "This share link has expired"
163
+ ? "This share link has expired (request {requestId}). Time for a fresh one 🌷"
93
164
  : refusal === "revoked"
94
- ? "This share link's key has been revoked"
165
+ ? "This share link's key has been revoked (request {requestId}). Ask for a new one 📩"
95
166
  : refusal === "disabled"
96
- ? "This share link's key is paused"
97
- : "Share link is unavailable";
167
+ ? "This share link's key is paused (request {requestId}). Re-enable it when ready 🌱"
168
+ : "Share link is unavailable (request {requestId}). This link leads nowhere — check it again 🔗";
98
169
  const error = new GatewayError(`link_${refusal}`, 404, message);
99
170
  return json(publicGatewayErrorBody(error), error.status);
100
171
  }
@@ -261,8 +332,14 @@ async function resolveFamilyStats(
261
332
  }
262
333
 
263
334
  /** Creates the public enrollment page and one-time shared-key issuance route. */
264
- export function createShareRouter(options: ShareRouterOptions): Elysia {
335
+ export function createShareRouter(options: ShareRouterOptions): AnyElysia {
265
336
  const { db, shareStore } = options;
337
+ // The sampler is shared by every stats stream this router serves; it is a
338
+ // no-op when the composition omits `stats` (tests that do not exercise the
339
+ // stream), because `tick` never runs with no subscribers.
340
+ const statsSampler = options.stats === undefined
341
+ ? undefined
342
+ : createShareStatsSampler({ db, shareStore, stats: options.stats });
266
343
 
267
344
  return new Elysia()
268
345
  /**
@@ -296,6 +373,7 @@ export function createShareRouter(options: ShareRouterOptions): Elysia {
296
373
  oneTimeLimit: row.lifetimeTokenBudget,
297
374
  requestsPerMinute: row.requestsPerMinute,
298
375
  maxConcurrentRequests: row.maxConcurrentRequests,
376
+ concurrencyScope: row.concurrencyScope ?? "family",
299
377
  modelPrefix: row.modelPrefix,
300
378
  models,
301
379
  modelInfo,
@@ -442,49 +520,39 @@ export function createShareRouter(options: ShareRouterOptions): Elysia {
442
520
  /**
443
521
  * Live family activity for the share page's stats section, pushed over SSE.
444
522
  *
445
- * The stream sends one snapshot immediately on connect, then re-reads on a
446
- * short cadence so the recipient watches the shared quota move instead of
447
- * refreshing. Telemetry is buffered server-side and drained in under a
448
- * second, so a DB re-read on this cadence is as fresh as an event bus would
449
- * be. Same bearer token and same masked-IP rollup as the one-shot route; a
450
- * token that stops resolving closes the stream rather than reporting zeros.
523
+ * The stream sends one snapshot immediately on connect, then pushes on a
524
+ * shared sampler cadence so the recipient watches the shared quota move
525
+ * instead of refreshing. Telemetry is buffered server-side and drained in
526
+ * under a second, so the sampler's DB re-read is as fresh as an event bus
527
+ * would be. Same bearer token and same masked-IP rollup as the one-shot
528
+ * route; a token that stops resolving closes the stream rather than
529
+ * reporting zeros.
451
530
  */
452
531
  .get("/share/:token/stats/stream", async ({ params, request }) => {
453
532
  if (typeof params.token !== "string") return notFound();
454
533
  const token = params.token;
455
534
  if (token.length < MIN_TOKEN_LENGTH) return notFound();
456
535
  const stats = options.stats;
457
- if (!stats) return json({ error: { code: "stats_unavailable", message: "Stats are unavailable" } }, 503);
536
+ if (!stats || statsSampler === undefined) {
537
+ return json({ error: { code: "stats_unavailable", message: "Stats are unavailable" } }, 503);
538
+ }
539
+ const sampler = statsSampler;
458
540
  return consoleSseResponse(
459
541
  createConsoleSseStream(request.signal, (sender) => {
460
- let closed = false;
461
- let timer: ReturnType<typeof setTimeout> | undefined;
462
- const push = async () => {
463
- const family = await resolveFamilyStats(db, shareStore, stats, token);
464
- if (closed) return;
542
+ const unsubscribe = sampler.subscribe(token, (family) => sender.send("stats", family));
543
+ // Bootstrap: deliver the link's current rollup immediately, and close
544
+ // the stream if the token no longer resolves — the same terminal
545
+ // behavior the per-connection timer had on a dead link.
546
+ void resolveFamilyStats(db, shareStore, stats, token).then((family) => {
465
547
  if (family === null) {
466
548
  sender.send("error", { code: "link_not_found" });
467
- closed = true;
468
- return;
549
+ unsubscribe();
550
+ } else {
551
+ sender.send("stats", family);
469
552
  }
470
- sender.send("stats", family);
471
- };
472
- const schedule = () => {
473
- timer = setTimeout(() => {
474
- if (closed) return;
475
- void push().finally(() => {
476
- if (!closed) schedule();
477
- });
478
- }, SHARE_STATS_STREAM_INTERVAL_MS);
479
- };
480
- void push().finally(() => {
481
- if (!closed) schedule();
482
- });
483
- return () => {
484
- closed = true;
485
- clearTimeout(timer);
486
- };
553
+ }).catch(() => undefined);
554
+ return unsubscribe;
487
555
  }),
488
556
  );
489
- }) as unknown as Elysia;
557
+ })
490
558
  }