cartethyia 2.7.0 → 2.7.2

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 (330) hide show
  1. package/.env.example +107 -106
  2. package/README.md +25 -2
  3. package/dist/dashboard/assets/{BannedPage-P76QLGJA.js → BannedPage-XaUmiBrC.js} +1 -1
  4. package/dist/dashboard/assets/BannedPage-XaUmiBrC.js.gz +0 -0
  5. package/dist/dashboard/assets/{CliToolDetailPage-DdBggJS9.js → CliToolDetailPage-CqD7QiLM.js} +1 -1
  6. package/dist/dashboard/assets/CliToolDetailPage-CqD7QiLM.js.gz +0 -0
  7. package/dist/dashboard/assets/{CliToolsPage-aN1iXhhP.js → CliToolsPage-0XUo4UMn.js} +1 -1
  8. package/dist/dashboard/assets/CliToolsPage-0XUo4UMn.js.gz +0 -0
  9. package/dist/dashboard/assets/{CombosPage-DE-F1nxZ.js → CombosPage-CXGS_Ct-.js} +1 -1
  10. package/dist/dashboard/assets/CombosPage-CXGS_Ct-.js.gz +0 -0
  11. package/dist/dashboard/assets/ConsoleLogPage-DGnGNYnM.js +1 -0
  12. package/dist/dashboard/assets/ConsoleLogPage-DGnGNYnM.js.gz +0 -0
  13. package/dist/dashboard/assets/{CustomizationPage-3CUxMy2X.js → CustomizationPage-DAG5J9EY.js} +1 -1
  14. package/dist/dashboard/assets/CustomizationPage-DAG5J9EY.js.gz +0 -0
  15. package/dist/dashboard/assets/HealthEventsModal-BKtAPYVF.js +1 -0
  16. package/dist/dashboard/assets/HealthEventsModal-BKtAPYVF.js.gz +0 -0
  17. package/dist/dashboard/assets/LoginPage-BdmRLnq2.js +1 -0
  18. package/dist/dashboard/assets/LoginPage-BdmRLnq2.js.gz +0 -0
  19. package/dist/dashboard/assets/{ModelPicker-DKVslqG3.js → ModelPicker-BH8Tehdi.js} +1 -1
  20. package/dist/dashboard/assets/ModelPicker-BH8Tehdi.js.gz +0 -0
  21. package/dist/dashboard/assets/OverviewPage-CqMxibJB.js +1 -0
  22. package/dist/dashboard/assets/OverviewPage-CqMxibJB.js.gz +0 -0
  23. package/dist/dashboard/assets/ProviderDetailPage-CqMh2NRX.js +4 -0
  24. package/dist/dashboard/assets/ProviderDetailPage-CqMh2NRX.js.gz +0 -0
  25. package/dist/dashboard/assets/ProviderIcon-FB6yS4v1.js +1 -0
  26. package/dist/dashboard/assets/ProviderIcon-FB6yS4v1.js.gz +0 -0
  27. package/dist/dashboard/assets/ProvidersPage-B9uWu4Nz.js +1 -0
  28. package/dist/dashboard/assets/ProvidersPage-B9uWu4Nz.js.gz +0 -0
  29. package/dist/dashboard/assets/ProxyPage-BoWOhpDO.js +9 -0
  30. package/dist/dashboard/assets/ProxyPage-BoWOhpDO.js.gz +0 -0
  31. package/dist/dashboard/assets/{QuotaPage-BjldQwIU.js → QuotaPage-7P2n--Fv.js} +1 -1
  32. package/dist/dashboard/assets/QuotaPage-7P2n--Fv.js.gz +0 -0
  33. package/dist/dashboard/assets/{SettingsPage-DUIoLRxk.js → SettingsPage-CxZ1fTkN.js} +1 -1
  34. package/dist/dashboard/assets/SettingsPage-CxZ1fTkN.js.gz +0 -0
  35. package/dist/dashboard/assets/SetupPage-Co12Zzaw.js +1 -0
  36. package/dist/dashboard/assets/SetupPage-Co12Zzaw.js.gz +0 -0
  37. package/dist/dashboard/assets/{SortableList-fI7JkdIB.js → SortableList-Dqf4zpS-.js} +1 -1
  38. package/dist/dashboard/assets/SortableList-Dqf4zpS-.js.gz +0 -0
  39. package/dist/dashboard/assets/{StudioPage-CHVZqskZ.js → StudioPage-Byj2lAEm.js} +3 -3
  40. package/dist/dashboard/assets/StudioPage-Byj2lAEm.js.gz +0 -0
  41. package/dist/dashboard/assets/UsagePage-CTkLAuRc.js +56 -0
  42. package/dist/dashboard/assets/UsagePage-CTkLAuRc.js.gz +0 -0
  43. package/dist/dashboard/assets/{api-keys-DFY_fZU4.js → api-keys-CUi6UXsR.js} +1 -1
  44. package/dist/dashboard/assets/api-keys-CUi6UXsR.js.gz +0 -0
  45. package/dist/dashboard/assets/app-KvsUJUMF.css +2 -0
  46. package/dist/dashboard/assets/app-KvsUJUMF.css.gz +0 -0
  47. package/dist/dashboard/assets/{assert-CwdnZARt.js → assert-67xXH5rf.js} +1 -1
  48. package/dist/dashboard/assets/assert-67xXH5rf.js.gz +0 -0
  49. package/dist/dashboard/assets/{badge-Dr-uf0Au.js → badge-DC2L43Mj.js} +1 -1
  50. package/dist/dashboard/assets/badge-DC2L43Mj.js.gz +0 -0
  51. package/dist/dashboard/assets/{banner-BddmMdoS.js → banner-BBVcb0J3.js} +1 -1
  52. package/dist/dashboard/assets/banner-BBVcb0J3.js.gz +0 -0
  53. package/dist/dashboard/assets/{card-CK0PXWlX.js → card-Cca1XC6G.js} +1 -1
  54. package/dist/dashboard/assets/card-Cca1XC6G.js.gz +0 -0
  55. package/dist/dashboard/assets/{cli-tools-CcB6mGh2.js → cli-tools-KvsWa1Jl.js} +1 -1
  56. package/dist/dashboard/assets/cli-tools-KvsWa1Jl.js.gz +0 -0
  57. package/dist/dashboard/assets/{clipboard-button-DGb2P9Yx.js → clipboard-button-DUzEbynq.js} +1 -1
  58. package/dist/dashboard/assets/clipboard-button-DUzEbynq.js.gz +0 -0
  59. package/dist/dashboard/assets/{dialog-LkfuL50u.js → dialog-DFvWhwTa.js} +1 -1
  60. package/dist/dashboard/assets/dialog-DFvWhwTa.js.gz +0 -0
  61. package/dist/dashboard/assets/{drawer-ZbEZ_84c.js → drawer-CFM2sMX1.js} +1 -1
  62. package/dist/dashboard/assets/drawer-CFM2sMX1.js.gz +0 -0
  63. package/dist/dashboard/assets/{entry-BJfsVMvA.js → entry-Bqy8RFvM.js} +1 -1
  64. package/dist/dashboard/assets/entry-Bqy8RFvM.js.gz +0 -0
  65. package/dist/dashboard/assets/{entry-CWBY5A0f.js → entry-ClCMCWWQ.js} +3 -3
  66. package/dist/dashboard/assets/entry-ClCMCWWQ.js.gz +0 -0
  67. package/dist/dashboard/assets/{entry-CvC1-Zqx.js → entry-_xHo5x6r.js} +1 -1
  68. package/dist/dashboard/assets/entry-_xHo5x6r.js.gz +0 -0
  69. package/dist/dashboard/assets/formatters-CWWZ-uCZ.js +1 -0
  70. package/dist/dashboard/assets/formatters-CWWZ-uCZ.js.gz +0 -0
  71. package/dist/dashboard/assets/{github-badge-BxpznFLt.js → github-badge-C9e8Ts7e.js} +1 -1
  72. package/dist/dashboard/assets/github-badge-C9e8Ts7e.js.gz +0 -0
  73. package/dist/dashboard/assets/{index-CDzP3ovJ.js → index-DFVFrnWW.js} +2 -2
  74. package/dist/dashboard/assets/index-DFVFrnWW.js.gz +0 -0
  75. package/dist/dashboard/assets/{inline-Ae13MQ5w.js → inline-BFUcvllj.js} +1 -1
  76. package/dist/dashboard/assets/inline-BFUcvllj.js.gz +0 -0
  77. package/dist/dashboard/assets/{input-D23fo7cp.js → input-C_ILAvBb.js} +1 -1
  78. package/dist/dashboard/assets/input-C_ILAvBb.js.gz +0 -0
  79. package/dist/dashboard/assets/{layout-CSsxjj5P.js → layout-CBIyT6Rt.js} +1 -1
  80. package/dist/dashboard/assets/layout-CBIyT6Rt.js.gz +0 -0
  81. package/dist/dashboard/assets/{list-row-dmbwZMDY.js → list-row-ebJ_-Nsb.js} +1 -1
  82. package/dist/dashboard/assets/list-row-ebJ_-Nsb.js.gz +0 -0
  83. package/dist/dashboard/assets/{live-BG1oUsZZ.js → live-D0mm--ME.js} +1 -1
  84. package/dist/dashboard/assets/live-D0mm--ME.js.gz +0 -0
  85. package/dist/dashboard/assets/{mutation-DSF-e4ho.js → mutation-sMD965oO.js} +1 -1
  86. package/dist/dashboard/assets/mutation-sMD965oO.js.gz +0 -0
  87. package/dist/dashboard/assets/{numbers-Bm_aECY7.js → numbers-BO9dKaFr.js} +1 -1
  88. package/dist/dashboard/assets/numbers-BO9dKaFr.js.gz +0 -0
  89. package/dist/dashboard/assets/providers/inception.svg +5 -0
  90. package/dist/dashboard/assets/providers/inception.svg.gz +0 -0
  91. package/dist/dashboard/assets/{query-keys-CF56nYIV.js → query-keys-CfL_Zdoh.js} +1 -1
  92. package/dist/dashboard/assets/query-keys-CfL_Zdoh.js.gz +0 -0
  93. package/dist/dashboard/assets/{routing-CeA71n7G.js → routing-BnIpi1IJ.js} +1 -1
  94. package/dist/dashboard/assets/routing-BnIpi1IJ.js.gz +0 -0
  95. package/dist/dashboard/assets/{select-KIT0rC6G.js → select-DBNVLdUu.js} +1 -1
  96. package/dist/dashboard/assets/select-DBNVLdUu.js.gz +0 -0
  97. package/dist/dashboard/assets/{settings-B_2gkePf.js → settings-BAFhDgX8.js} +1 -1
  98. package/dist/dashboard/assets/settings-BAFhDgX8.js.gz +0 -0
  99. package/dist/dashboard/assets/{stack-D7WOSzcX.js → stack-EMrOMt4I.js} +1 -1
  100. package/dist/dashboard/assets/stack-EMrOMt4I.js.gz +0 -0
  101. package/dist/dashboard/assets/{state-BBxtKZOQ.js → state-CWoo7ty5.js} +1 -1
  102. package/dist/dashboard/assets/state-CWoo7ty5.js.gz +0 -0
  103. package/dist/dashboard/assets/{switch-DNS_D5Ea.js → switch-CCjT6IkF.js} +1 -1
  104. package/dist/dashboard/assets/switch-CCjT6IkF.js.gz +0 -0
  105. package/dist/dashboard/assets/{useMutation-BgoxdoMk.js → useMutation-DjkLHUcE.js} +1 -1
  106. package/dist/dashboard/assets/useMutation-DjkLHUcE.js.gz +0 -0
  107. package/dist/dashboard/index.html +1 -1
  108. package/dist/dashboard/index.html.gz +0 -0
  109. package/dist/dashboard/precompressed-assets.json +54 -53
  110. package/migrations/full-postgres/0024_add_pool_provider_cooldowns.sql +12 -0
  111. package/migrations/full-postgres/0025_add_pool_random_strategy.sql +3 -0
  112. package/migrations/lite-sqlite3/0024_add_pool_provider_cooldowns.sql +12 -0
  113. package/migrations/lite-sqlite3/0025_add_pool_random_strategy.sql +20 -0
  114. package/package.json +96 -96
  115. package/scripts/cli/console-menu.ts +5 -2
  116. package/scripts/cli/tray.ts +21 -6
  117. package/src/boot/app.ts +684 -714
  118. package/src/boot/dependencies.ts +65 -62
  119. package/src/config/cooldowns.ts +16 -0
  120. package/src/config/providers.ts +13 -1
  121. package/src/config/resources.ts +70 -70
  122. package/src/config/security.ts +37 -37
  123. package/src/config/spec.ts +371 -358
  124. package/src/console/cli-tools/contracts.ts +10 -4
  125. package/src/console/console-router.ts +233 -225
  126. package/src/console/domain-registration.ts +398 -381
  127. package/src/console/providers/catalog/catalog-projections.ts +2 -6
  128. package/src/console/providers/catalog/contracts.ts +19 -0
  129. package/src/console/providers/catalog/provider-operations.ts +38 -0
  130. package/src/console/providers/catalog/routes.ts +14 -0
  131. package/src/console/providers/catalog/store-helpers.ts +326 -0
  132. package/src/console/providers/catalog/store.ts +1292 -1563
  133. package/src/console/quota/account-quota-global.ts +2 -2
  134. package/src/console/quota/account-quota-shared.ts +1 -1
  135. package/src/console/quota/account-quota-tenant.ts +35 -12
  136. package/src/console/quota/quota-view.ts +2 -2
  137. package/src/console/routing/pools/contracts.ts +41 -1
  138. package/src/console/routing/pools/operations.ts +183 -4
  139. package/src/console/routing/pools/relay-deploy.ts +17 -4
  140. package/src/console/routing/pools/routes.ts +30 -0
  141. package/src/console/routing/pools/store.ts +98 -8
  142. package/src/main.ts +183 -167
  143. package/src/network/outbound-fetch.ts +124 -208
  144. package/src/network/pool/agent.ts +15 -0
  145. package/src/network/pool/pool-state.ts +7 -0
  146. package/src/network/pool/selector.ts +110 -2
  147. package/src/network/pool-cooldown-store.ts +252 -0
  148. package/src/network/pool-health-machine.ts +5 -3
  149. package/src/network/pool-health.ts +36 -9
  150. package/src/observability/event-query.ts +57 -0
  151. package/src/observability/metrics.ts +467 -467
  152. package/src/observability/runtime-metrics.ts +269 -269
  153. package/src/persistence/health-event-flush.ts +2 -0
  154. package/src/persistence/schema.ts +19 -0
  155. package/src/persistence/usage-flush.ts +8 -3
  156. package/src/protocol/canonical-model.ts +1 -1
  157. package/src/protocol/dsml.ts +377 -0
  158. package/src/protocol/gateway-error.ts +232 -232
  159. package/src/{providers/provider-reasoning.ts → protocol/reasoning.ts} +10 -9
  160. package/src/protocol/request/chat.ts +7 -7
  161. package/src/protocol/request/codex.ts +2 -2
  162. package/src/protocol/request/gemini.ts +1 -1
  163. package/src/protocol/request/messages.ts +6 -6
  164. package/src/protocol/request/responses.ts +7 -7
  165. package/src/protocol/response/dsml.ts +12 -133
  166. package/src/{transport → protocol}/translation/attachment-integrity.ts +2 -2
  167. package/src/{transport → protocol}/translation/cache-controls.ts +1 -1
  168. package/src/{transport → protocol}/translation/capabilities.ts +2 -2
  169. package/src/{transport → protocol}/translation/normalize.ts +1 -1
  170. package/src/{transport → protocol}/translation/quirks.ts +1 -1
  171. package/src/{transport → protocol}/translation/search.ts +1 -1
  172. package/src/{transport → protocol}/translation/thinking.ts +23 -1
  173. package/src/{transport → protocol}/translation/tool-id.ts +1 -1
  174. package/src/{transport → protocol}/translation/tool-repair.ts +2 -2
  175. package/src/protocol/upstream-parsing.ts +176 -0
  176. package/src/providers/adapters/compatible-adapter.ts +11 -2
  177. package/src/providers/authentication/oauth-refresh-service.ts +9 -5
  178. package/src/providers/default-registry.ts +22 -5
  179. package/src/providers/discovery/probe-phases.ts +16 -3
  180. package/src/providers/discovery/probing-service.ts +4 -1
  181. package/src/providers/integrations/anthropic.ts +1 -1
  182. package/src/providers/integrations/buddy/buddy-catalog-shared.ts +1 -4
  183. package/src/providers/integrations/buddy/buddy-chat-shared.ts +141 -8
  184. package/src/providers/integrations/buddy/buddy-checkin.ts +5 -1
  185. package/src/providers/integrations/buddy/buddy-growth-shared.ts +0 -82
  186. package/src/providers/integrations/buddy/buddy-identity.ts +25 -0
  187. package/src/providers/integrations/buddy/buddy-server-tools.ts +240 -0
  188. package/src/providers/integrations/buddy/codebuddy/cn-catalog.ts +36 -0
  189. package/src/providers/integrations/buddy/codebuddy/cn-provider.ts +6 -31
  190. package/src/providers/integrations/buddy/codebuddy/provider.ts +2 -0
  191. package/src/providers/integrations/buddy/codebuddy/shared.ts +19 -4
  192. package/src/providers/integrations/buddy/turing-device-token.ts +262 -0
  193. package/src/providers/integrations/buddy/workbuddy/provider.ts +2 -0
  194. package/src/providers/integrations/buddy/workbuddy/shared.ts +56 -3
  195. package/src/providers/integrations/claude/claude.ts +1 -1
  196. package/src/providers/integrations/cloudflareai/cloudflareai.ts +1 -1
  197. package/src/providers/integrations/codex/codex-errors.ts +1 -1
  198. package/src/providers/integrations/codex/codex.ts +1 -1
  199. package/src/providers/integrations/factory/factory.ts +1 -1
  200. package/src/providers/integrations/github/github.ts +1 -1
  201. package/src/providers/integrations/grok/grok.ts +1 -1
  202. package/src/providers/integrations/inception/provider.ts +91 -0
  203. package/src/providers/integrations/inferhub.ts +1 -1
  204. package/src/providers/integrations/kiro/kiro-request.ts +1 -1
  205. package/src/providers/integrations/mistral.ts +193 -19
  206. package/src/providers/integrations/ollamacloud-models.ts +50 -0
  207. package/src/providers/integrations/opencode/fingerprint.ts +19 -2
  208. package/src/providers/integrations/opencode/prompt.ts +111 -0
  209. package/src/providers/integrations/opencode/provider.ts +101 -23
  210. package/src/providers/integrations/opencode/tool-definitions.ts +4 -4
  211. package/src/providers/integrations/qoder/qoder-checkin.ts +303 -0
  212. package/src/providers/integrations/qoder/qoder-oauth.ts +12 -1
  213. package/src/providers/integrations/qoder/qoder-realm.ts +20 -0
  214. package/src/providers/integrations/qoder/qoder-text-tools.ts +277 -0
  215. package/src/providers/integrations/qoder/qoder.ts +315 -400
  216. package/src/providers/integrations/shared/install-id.ts +2 -1
  217. package/src/providers/integrations/trae/trae-cache.ts +110 -0
  218. package/src/providers/integrations/trae/trae-oauth.ts +29 -4
  219. package/src/providers/integrations/trae/trae-protocol.ts +2 -1
  220. package/src/providers/integrations/trae/trae.ts +106 -23
  221. package/src/providers/operations/account-error-classification.ts +1684 -0
  222. package/src/providers/operations/account-health-service.ts +27 -1503
  223. package/src/providers/operations/account-reset-service.ts +2 -0
  224. package/src/providers/operations/provider-catalog-service.ts +11 -1
  225. package/src/providers/provider-contracts.ts +7 -4
  226. package/src/providers/provider-metadata.ts +25 -4
  227. package/src/providers/provider-probe.ts +2 -2
  228. package/src/{console → providers}/quota/cache.ts +1 -1
  229. package/src/{console → providers}/quota/refresh.ts +5 -5
  230. package/src/security/cli-client-fingerprint.ts +9 -2
  231. package/src/transport/dispatch/attempt-finalize.ts +114 -12
  232. package/src/transport/dispatch/attempt-loop.ts +3 -0
  233. package/src/transport/dispatch/empty-stream.ts +9 -2
  234. package/src/transport/dispatch/generation.ts +37 -55
  235. package/src/transport/dispatch/handler-deps.ts +32 -0
  236. package/src/transport/dispatch/leases.ts +9 -4
  237. package/src/transport/dispatch/proxy-request.ts +6 -30
  238. package/src/transport/dispatch/responses-compact.ts +22 -43
  239. package/src/transport/dispatch/streaming/finalize.ts +3 -0
  240. package/src/transport/dispatch/streaming/stream.ts +0 -8
  241. package/src/transport/dispatch/systemone.ts +22 -42
  242. package/src/transport/dispatch/token-count.ts +23 -39
  243. package/src/transport/dispatch/upstream.ts +1 -3
  244. package/src/transport/dispatch/websearch.ts +22 -41
  245. package/src/transport/failure-policy.ts +92 -178
  246. package/src/transport/guards/abuse-guard.ts +10 -0
  247. package/src/transport/guards/auth-guard.ts +109 -109
  248. package/src/transport/guards/error-lifecycle.ts +1 -1
  249. package/src/transport/guards/pipeline.ts +220 -220
  250. package/src/transport/guards/request-context.ts +245 -238
  251. package/src/transport/request/degradation.ts +20 -1
  252. package/src/transport/request/inflight.ts +4 -114
  253. package/src/transport/request/preparer.ts +6 -6
  254. package/src/transport/request/state.ts +66 -38
  255. package/src/transport/request/tenant-preferences.ts +1 -1
  256. package/src/transport/routing/route-catalog.ts +75 -43
  257. package/src/transport/routing/router.ts +30 -2
  258. package/src/transport/surface/messages/parse.ts +1 -1
  259. package/src/transport/surface/responses/encode.ts +2 -3
  260. package/src/transport/surface/responses/parse.ts +49 -7
  261. package/src/types/undici.d.ts +33 -0
  262. package/src/workers/buddy-growth-tasks.ts +346 -425
  263. package/src/workers/daily-checkin.ts +17 -6
  264. package/src/workers/quota-refresh-sweep.ts +2 -2
  265. package/src/workers/scheduler.ts +17 -1
  266. package/dist/dashboard/assets/BannedPage-P76QLGJA.js.gz +0 -0
  267. package/dist/dashboard/assets/CliToolDetailPage-DdBggJS9.js.gz +0 -0
  268. package/dist/dashboard/assets/CliToolsPage-aN1iXhhP.js.gz +0 -0
  269. package/dist/dashboard/assets/CombosPage-DE-F1nxZ.js.gz +0 -0
  270. package/dist/dashboard/assets/ConsoleLogPage-5SpmfLyw.js +0 -1
  271. package/dist/dashboard/assets/ConsoleLogPage-5SpmfLyw.js.gz +0 -0
  272. package/dist/dashboard/assets/CustomizationPage-3CUxMy2X.js.gz +0 -0
  273. package/dist/dashboard/assets/HealthEventsModal-BkmJQKC0.js +0 -1
  274. package/dist/dashboard/assets/HealthEventsModal-BkmJQKC0.js.gz +0 -0
  275. package/dist/dashboard/assets/LoginPage-B_0FUKWc.js +0 -1
  276. package/dist/dashboard/assets/LoginPage-B_0FUKWc.js.gz +0 -0
  277. package/dist/dashboard/assets/ModelPicker-DKVslqG3.js.gz +0 -0
  278. package/dist/dashboard/assets/OverviewPage-D-H4MEiT.js +0 -1
  279. package/dist/dashboard/assets/OverviewPage-D-H4MEiT.js.gz +0 -0
  280. package/dist/dashboard/assets/ProviderDetailPage-CEJw6Guo.js +0 -3
  281. package/dist/dashboard/assets/ProviderDetailPage-CEJw6Guo.js.gz +0 -0
  282. package/dist/dashboard/assets/ProviderIcon-CmfzxpVM.js +0 -1
  283. package/dist/dashboard/assets/ProviderIcon-CmfzxpVM.js.gz +0 -0
  284. package/dist/dashboard/assets/ProvidersPage-CEplAR-i.js +0 -1
  285. package/dist/dashboard/assets/ProvidersPage-CEplAR-i.js.gz +0 -0
  286. package/dist/dashboard/assets/ProxyPage-CAN7hy0f.js +0 -7
  287. package/dist/dashboard/assets/ProxyPage-CAN7hy0f.js.gz +0 -0
  288. package/dist/dashboard/assets/QuotaPage-BjldQwIU.js.gz +0 -0
  289. package/dist/dashboard/assets/SettingsPage-DUIoLRxk.js.gz +0 -0
  290. package/dist/dashboard/assets/SetupPage-_bCaTM0v.js +0 -1
  291. package/dist/dashboard/assets/SetupPage-_bCaTM0v.js.gz +0 -0
  292. package/dist/dashboard/assets/SortableList-fI7JkdIB.js.gz +0 -0
  293. package/dist/dashboard/assets/StudioPage-CHVZqskZ.js.gz +0 -0
  294. package/dist/dashboard/assets/UsagePage-DiFBymHj.js +0 -56
  295. package/dist/dashboard/assets/UsagePage-DiFBymHj.js.gz +0 -0
  296. package/dist/dashboard/assets/api-keys-DFY_fZU4.js.gz +0 -0
  297. package/dist/dashboard/assets/app-BOJ1_xUB.css +0 -2
  298. package/dist/dashboard/assets/app-BOJ1_xUB.css.gz +0 -0
  299. package/dist/dashboard/assets/assert-CwdnZARt.js.gz +0 -0
  300. package/dist/dashboard/assets/badge-Dr-uf0Au.js.gz +0 -0
  301. package/dist/dashboard/assets/banner-BddmMdoS.js.gz +0 -0
  302. package/dist/dashboard/assets/card-CK0PXWlX.js.gz +0 -0
  303. package/dist/dashboard/assets/cli-tools-CcB6mGh2.js.gz +0 -0
  304. package/dist/dashboard/assets/clipboard-button-DGb2P9Yx.js.gz +0 -0
  305. package/dist/dashboard/assets/cog-CdWhsajs.js +0 -1
  306. package/dist/dashboard/assets/cog-CdWhsajs.js.gz +0 -0
  307. package/dist/dashboard/assets/dialog-LkfuL50u.js.gz +0 -0
  308. package/dist/dashboard/assets/drawer-ZbEZ_84c.js.gz +0 -0
  309. package/dist/dashboard/assets/entry-BJfsVMvA.js.gz +0 -0
  310. package/dist/dashboard/assets/entry-CWBY5A0f.js.gz +0 -0
  311. package/dist/dashboard/assets/entry-CvC1-Zqx.js.gz +0 -0
  312. package/dist/dashboard/assets/github-badge-BxpznFLt.js.gz +0 -0
  313. package/dist/dashboard/assets/index-CDzP3ovJ.js.gz +0 -0
  314. package/dist/dashboard/assets/inline-Ae13MQ5w.js.gz +0 -0
  315. package/dist/dashboard/assets/input-D23fo7cp.js.gz +0 -0
  316. package/dist/dashboard/assets/layout-CSsxjj5P.js.gz +0 -0
  317. package/dist/dashboard/assets/list-row-dmbwZMDY.js.gz +0 -0
  318. package/dist/dashboard/assets/live-BG1oUsZZ.js.gz +0 -0
  319. package/dist/dashboard/assets/mutation-DSF-e4ho.js.gz +0 -0
  320. package/dist/dashboard/assets/numbers-Bm_aECY7.js.gz +0 -0
  321. package/dist/dashboard/assets/query-keys-CF56nYIV.js.gz +0 -0
  322. package/dist/dashboard/assets/routing-CeA71n7G.js.gz +0 -0
  323. package/dist/dashboard/assets/select-KIT0rC6G.js.gz +0 -0
  324. package/dist/dashboard/assets/settings-B_2gkePf.js.gz +0 -0
  325. package/dist/dashboard/assets/stack-D7WOSzcX.js.gz +0 -0
  326. package/dist/dashboard/assets/state-BBxtKZOQ.js.gz +0 -0
  327. package/dist/dashboard/assets/switch-DNS_D5Ea.js.gz +0 -0
  328. package/dist/dashboard/assets/useMutation-BgoxdoMk.js.gz +0 -0
  329. /package/dist/dashboard/assets/{app-CaXZ9SCn.js → app-BaWd0puM.js} +0 -0
  330. /package/dist/dashboard/assets/{app-CaXZ9SCn.js.gz → app-BaWd0puM.js.gz} +0 -0
@@ -1,220 +1,220 @@
1
- import { Elysia } from "elysia";
2
- import { createIngressPolicyMiddleware } from "./body-policy";
3
- import {
4
- createCanonicalRequestMiddleware,
5
- createClientIdentityMiddleware,
6
- createProxyRoutePreparationMiddleware,
7
- createRequestContextMiddleware,
8
- } from "./request-context";
9
- import type { CanonicalAdapter, ShutdownDrainSource } from "../contracts";
10
- import { createApiKeyAuthenticationMiddleware } from "./auth-guard";
11
- import { createDependencyReadinessMiddleware } from "./readiness-guard";
12
- import { createIpAbuseProtectionMiddleware } from "./abuse-guard";
13
- import { createHeadroomGuard } from "./headroom-guard";
14
- import {
15
- createErrorNormalizationMiddleware,
16
- registerRequestCleanup,
17
- registerTelemetryLifecycle,
18
- type AfterResponseApp,
19
- } from "./error-lifecycle";
20
- import type { ReadinessCheckResult } from "../../persistence/readiness";
21
- import type { IpAbuseProtectionService } from "../../security/abuse";
22
- import type { ProxyRequestPreparer } from "../request/preparer";
23
- import { ProxyRequestStateStore } from "../request/state";
24
- import type { SurfaceAdapterRegistry } from "../surface/adapters";
25
- import type { TrustedProxyBoundary } from "../../config/network";
26
- import type { CartethyiaDatabase } from "../../persistence/db-handle";
27
- import type { RedisClient } from "../../persistence/redis";
28
- import type { TelemetryBatchBuffer } from "../../observability/telemetry-buffer";
29
- import type { RuntimeMemorySnapshot } from "../../observability/runtime-metrics";
30
- import {
31
- TelemetryPayloadCapture,
32
- payloadCaptureKeepsBodies,
33
- payloadRetentionMs,
34
- resolvePayloadCapture,
35
- } from "../../observability/payload-capture";
36
-
37
- /** Shared state/context carried by every ordered transport stage. */
38
- export interface TransportPipelineContext {
39
- readonly db: CartethyiaDatabase;
40
- readonly stateStore: ProxyRequestStateStore;
41
- readonly surfaceRegistry: SurfaceAdapterRegistry;
42
- readonly adapters: ReadonlyMap<string, CanonicalAdapter>;
43
- readonly preparer: ProxyRequestPreparer;
44
- readonly readiness: () => Promise<ReadinessCheckResult>;
45
- readonly trustedProxyBoundary: TrustedProxyBoundary;
46
- readonly resolvePeerAddress?: (request: Request) => string | null;
47
- readonly ipAbuseProtection?: IpAbuseProtectionService;
48
- readonly maxBodyBytes?: number;
49
- readonly requestDeadlineMs?: number;
50
- readonly verifiedHttps?: boolean;
51
- /** Graduated strikes for repeated invalid-model requests. */
52
- readonly shutdownCoordinator?: ShutdownDrainSource;
53
- readonly telemetry?: TelemetryBatchBuffer;
54
- /**
55
- * Shared client for the headroom guard's shed marker. Optional: the shed
56
- * decision is local, and Redis is only written for fleet visibility.
57
- */
58
- readonly redis?: RedisClient | undefined;
59
- /**
60
- * The sampled memory state the headroom guard sheds against.
61
- *
62
- * Supplied by the runtime sampler. When absent the guard cannot compare RSS
63
- * against a limit and does not shed — so this must be wired in production,
64
- * and the sampler must be initialized at boot even before its first tick.
65
- */
66
- readonly memorySnapshot?: () => RuntimeMemorySnapshot | undefined;
67
- /**
68
- * Whether invoke is switched off for a tenant. Optional: without it the
69
- * gateway never refuses on the kill switch, which is the pre-change
70
- * behaviour rather than a failure.
71
- */
72
- readonly isInvokeDisabled?: (tenantId: string) => Promise<boolean | undefined>;
73
- }
74
-
75
- /**
76
- * The single ingress owner. `mountRoot` applies the process-wide request
77
- * context and error-normalization lifecycle; `createGateway` composes the
78
- * ordered fail-closed `/v1/*` stage chain plus the telemetry/cleanup
79
- * lifecycle around caller-registered gateway routes. Composition code
80
- * (`app.ts`) mounts — it never assembles middleware policy itself.
81
- */
82
- export interface TransportPipeline {
83
- /** Applies root-scoped request context and error normalization. */
84
- mountRoot(app: Elysia<any, any, any, any, any, any, any, any>): void;
85
- /** Builds the complete `/v1/*` gateway plugin around registered routes. */
86
- createGateway(registerRoutes: (routes: Elysia<any, any, any, any, any, any, any, any>) => void): Elysia;
87
- }
88
-
89
- /**
90
- * Builds the one ordered middleware pipeline. Request context and error
91
- * normalization are mounted at the root; the remaining stages are mounted
92
- * before every `/v1/*` route, preserving fail-closed ordering.
93
- */
94
- export function createTransportPipeline(context: TransportPipelineContext): TransportPipeline {
95
- const stages: Elysia[] = [
96
- createHeadroomGuard({
97
- store: context.stateStore,
98
- ...(context.memorySnapshot === undefined ? {} : { memorySnapshot: context.memorySnapshot }),
99
- ...(context.redis === undefined ? {} : { redis: context.redis }),
100
- }),
101
- createDependencyReadinessMiddleware({
102
- readiness: context.readiness,
103
- ...(context.shutdownCoordinator ? { shutdownCoordinator: context.shutdownCoordinator } : {}),
104
- }),
105
- createIngressPolicyMiddleware({
106
- stateStore: context.stateStore,
107
- ...(context.maxBodyBytes === undefined ? {} : { maxBodyBytes: context.maxBodyBytes }),
108
- }),
109
- createClientIdentityMiddleware({
110
- stateStore: context.stateStore,
111
- trustedProxyBoundary: context.trustedProxyBoundary,
112
- ...(context.resolvePeerAddress ? { resolvePeerAddress: context.resolvePeerAddress } : {}),
113
- }),
114
- createApiKeyAuthenticationMiddleware({
115
- db: context.db,
116
- stateStore: context.stateStore,
117
- ...(context.isInvokeDisabled
118
- ? { isInvokeDisabled: context.isInvokeDisabled }
119
- : {}),
120
- }),
121
- createCanonicalRequestMiddleware({
122
- stateStore: context.stateStore,
123
- surfaceRegistry: context.surfaceRegistry,
124
- adapters: context.adapters,
125
- keepsIngressBody: (tenantId) => payloadCaptureKeepsBodies(context.db, tenantId),
126
- }),
127
- createProxyRoutePreparationMiddleware({
128
- stateStore: context.stateStore,
129
- preparer: context.preparer,
130
- }),
131
- ];
132
- const mountRoot = (app: Elysia<any, any, any, any, any, any, any, any>): void => {
133
- app.use(
134
- createRequestContextMiddleware({
135
- stateStore: context.stateStore,
136
- ...(context.requestDeadlineMs === undefined
137
- ? {}
138
- : { requestDeadlineMs: context.requestDeadlineMs }),
139
- }),
140
- );
141
- // Root-mounted, not a gateway stage: its `request` hook must run for every
142
- // `/v1/*` request, including paths that match no route, or a caller can
143
- // hammer the gateway without ever being counted. See the middleware's own
144
- // doc for the measured bypass this closes.
145
- if (context.ipAbuseProtection) {
146
- app.use(
147
- createIpAbuseProtectionMiddleware({
148
- stateStore: context.stateStore,
149
- ipAbuseProtection: context.ipAbuseProtection,
150
- trustedProxyBoundary: context.trustedProxyBoundary,
151
- ...(context.resolvePeerAddress ? { resolvePeerAddress: context.resolvePeerAddress } : {}),
152
- }),
153
- );
154
- }
155
- app.use(
156
- createErrorNormalizationMiddleware({
157
- stateStore: context.stateStore,
158
- hsts: context.verifiedHttps === true,
159
- }),
160
- );
161
- // Finalize + cleanup live at the ROOT, not on the gateway plugin.
162
- //
163
- // A plugin-scoped `afterResponse` only fires for a request that matched a
164
- // registered route, so an unregistered `/v1/*` path — the cheapest thing a
165
- // scanner can send — was admitted by the root `request` hook (which does
166
- // run for every inbound request, that is why the in-flight count and the
167
- // IP-abuse counter are there) but never cleaned up. Measured: one
168
- // unmatched `/v1/*` request left `proxy_in_flight` permanently higher and
169
- // `activeCount()` permanently one larger, so the gauge only ever climbed
170
- // for the life of the process. The root hook runs for the unmatched path
171
- // too, which is what makes the increment and the decrement symmetric.
172
- //
173
- // Ordering is safe because a root `afterResponse` still runs *after* the
174
- // matched handler returns (measured: request -> handler -> root.afterResponse
175
- // -> plugin.afterResponse), so a dispatch route is finalized exactly as
176
- // before. Registering it only here — never also on the gateway — is what
177
- // keeps a matched route from finalizing twice.
178
- // `mountRoot` receives a fully-widened `Elysia<any, ...>`, whose overloaded
179
- // `afterResponse` signature is not structurally assignable to the minimal
180
- // `AfterResponseApp` hook interface even though the runtime shape matches
181
- // (Elysia passes `{ request }`). The cast is confined to this one call.
182
- const rootApp = app as unknown as AfterResponseApp;
183
- if (context.telemetry) {
184
- registerTelemetryLifecycle(rootApp, {
185
- stateStore: context.stateStore,
186
- telemetryBuffer: context.telemetry,
187
- // One capture for the process, shared with every request state so an
188
- // early rejection can record its payload from the finalize hook. It
189
- // resolves the tenant's retention itself because an early rejection has
190
- // no preference memo on its state — nothing resolved one before the
191
- // request was refused.
192
- payloadCapture: new TelemetryPayloadCapture(context.db, (tenantId) =>
193
- resolvePayloadCapture(context.db, tenantId).then(
194
- (resolved) => (resolved.ok ? resolved.retentionMs : payloadRetentionMs()),
195
- ),
196
- ),
197
- // The early-rejection capture resolves the tenant's capture mode
198
- // through the same preference path dispatched requests use, instead of
199
- // capturing bodies under the deployment default regardless of the
200
- // tenant's none/metadata choice.
201
- db: context.db,
202
- });
203
- } else {
204
- registerRequestCleanup(rootApp, { stateStore: context.stateStore });
205
- }
206
- };
207
- const createGateway = (
208
- registerRoutes: (routes: Elysia<any, any, any, any, any, any, any, any>) => void,
209
- ): Elysia<any, any, any, any, any, any, any, any> => {
210
- const gateway = new Elysia({ prefix: "/v1" });
211
- for (const plugin of stages) gateway.use(plugin);
212
- // No telemetry/cleanup registration here: the lifecycle is owned by
213
- // `mountRoot`. Registering a second copy on this plugin would finalize a
214
- // matched request twice, and relying on this copy alone would leak every
215
- // unmatched `/v1/*` request (see `mountRoot`).
216
- registerRoutes(gateway);
217
- return gateway;
218
- };
219
- return { mountRoot, createGateway };
220
- }
1
+ import { Elysia } from "elysia";
2
+ import { createIngressPolicyMiddleware } from "./body-policy";
3
+ import {
4
+ createCanonicalRequestMiddleware,
5
+ createClientIdentityMiddleware,
6
+ createProxyRoutePreparationMiddleware,
7
+ createRequestContextMiddleware,
8
+ } from "./request-context";
9
+ import type { CanonicalAdapter, ShutdownDrainSource } from "../contracts";
10
+ import { createApiKeyAuthenticationMiddleware } from "./auth-guard";
11
+ import { createDependencyReadinessMiddleware } from "./readiness-guard";
12
+ import { createIpAbuseProtectionMiddleware } from "./abuse-guard";
13
+ import { createHeadroomGuard } from "./headroom-guard";
14
+ import {
15
+ createErrorNormalizationMiddleware,
16
+ registerRequestCleanup,
17
+ registerTelemetryLifecycle,
18
+ type AfterResponseApp,
19
+ } from "./error-lifecycle";
20
+ import type { ReadinessCheckResult } from "../../persistence/readiness";
21
+ import type { IpAbuseProtectionService } from "../../security/abuse";
22
+ import type { ProxyRequestPreparer } from "../request/preparer";
23
+ import { ProxyRequestStateStore } from "../request/state";
24
+ import type { SurfaceAdapterRegistry } from "../surface/adapters";
25
+ import type { TrustedProxyBoundary } from "../../config/network";
26
+ import type { CartethyiaDatabase } from "../../persistence/db-handle";
27
+ import type { RedisClient } from "../../persistence/redis";
28
+ import type { TelemetryBatchBuffer } from "../../observability/telemetry-buffer";
29
+ import type { RuntimeMemorySnapshot } from "../../observability/runtime-metrics";
30
+ import {
31
+ TelemetryPayloadCapture,
32
+ payloadCaptureKeepsBodies,
33
+ payloadRetentionMs,
34
+ resolvePayloadCapture,
35
+ } from "../../observability/payload-capture";
36
+
37
+ /** Shared state/context carried by every ordered transport stage. */
38
+ export interface TransportPipelineContext {
39
+ readonly db: CartethyiaDatabase;
40
+ readonly stateStore: ProxyRequestStateStore;
41
+ readonly surfaceRegistry: SurfaceAdapterRegistry;
42
+ readonly adapters: ReadonlyMap<string, CanonicalAdapter>;
43
+ readonly preparer: ProxyRequestPreparer;
44
+ readonly readiness: () => Promise<ReadinessCheckResult>;
45
+ readonly trustedProxyBoundary: TrustedProxyBoundary;
46
+ readonly resolvePeerAddress?: (request: Request) => string | null;
47
+ readonly ipAbuseProtection?: IpAbuseProtectionService;
48
+ readonly maxBodyBytes?: number;
49
+ readonly requestDeadlineMs?: number;
50
+ readonly verifiedHttps?: boolean;
51
+ /** Graduated strikes for repeated invalid-model requests. */
52
+ readonly shutdownCoordinator?: ShutdownDrainSource;
53
+ readonly telemetry?: TelemetryBatchBuffer;
54
+ /**
55
+ * Shared client for the headroom guard's shed marker. Optional: the shed
56
+ * decision is local, and Redis is only written for fleet visibility.
57
+ */
58
+ readonly redis?: RedisClient | undefined;
59
+ /**
60
+ * The sampled memory state the headroom guard sheds against.
61
+ *
62
+ * Supplied by the runtime sampler. When absent the guard cannot compare RSS
63
+ * against a limit and does not shed — so this must be wired in production,
64
+ * and the sampler must be initialized at boot even before its first tick.
65
+ */
66
+ readonly memorySnapshot?: () => RuntimeMemorySnapshot | undefined;
67
+ /**
68
+ * Whether invoke is switched off for a tenant. Optional: without it the
69
+ * gateway never refuses on the kill switch, which is the pre-change
70
+ * behaviour rather than a failure.
71
+ */
72
+ readonly isInvokeDisabled?: (tenantId: string) => Promise<boolean | undefined>;
73
+ }
74
+
75
+ /**
76
+ * The single ingress owner. `mountRoot` applies the process-wide request
77
+ * context and error-normalization lifecycle; `createGateway` composes the
78
+ * ordered fail-closed `/v1/*` stage chain plus the telemetry/cleanup
79
+ * lifecycle around caller-registered gateway routes. Composition code
80
+ * (`app.ts`) mounts — it never assembles middleware policy itself.
81
+ */
82
+ export interface TransportPipeline {
83
+ /** Applies root-scoped request context and error normalization. */
84
+ mountRoot(app: Elysia<any, any, any, any, any, any, any, any>): void;
85
+ /** Builds the complete `/v1/*` gateway plugin around registered routes. */
86
+ createGateway(registerRoutes: (routes: Elysia<any, any, any, any, any, any, any, any>) => void): Elysia;
87
+ }
88
+
89
+ /**
90
+ * Builds the one ordered middleware pipeline. Request context and error
91
+ * normalization are mounted at the root; the remaining stages are mounted
92
+ * before every `/v1/*` route, preserving fail-closed ordering.
93
+ */
94
+ export function createTransportPipeline(context: TransportPipelineContext): TransportPipeline {
95
+ const stages: Elysia[] = [
96
+ createHeadroomGuard({
97
+ store: context.stateStore,
98
+ ...(context.memorySnapshot === undefined ? {} : { memorySnapshot: context.memorySnapshot }),
99
+ ...(context.redis === undefined ? {} : { redis: context.redis }),
100
+ }),
101
+ createDependencyReadinessMiddleware({
102
+ readiness: context.readiness,
103
+ ...(context.shutdownCoordinator ? { shutdownCoordinator: context.shutdownCoordinator } : {}),
104
+ }),
105
+ createIngressPolicyMiddleware({
106
+ stateStore: context.stateStore,
107
+ ...(context.maxBodyBytes === undefined ? {} : { maxBodyBytes: context.maxBodyBytes }),
108
+ }),
109
+ createClientIdentityMiddleware({
110
+ stateStore: context.stateStore,
111
+ trustedProxyBoundary: context.trustedProxyBoundary,
112
+ ...(context.resolvePeerAddress ? { resolvePeerAddress: context.resolvePeerAddress } : {}),
113
+ }),
114
+ createApiKeyAuthenticationMiddleware({
115
+ db: context.db,
116
+ stateStore: context.stateStore,
117
+ ...(context.isInvokeDisabled
118
+ ? { isInvokeDisabled: context.isInvokeDisabled }
119
+ : {}),
120
+ }),
121
+ createCanonicalRequestMiddleware({
122
+ stateStore: context.stateStore,
123
+ surfaceRegistry: context.surfaceRegistry,
124
+ adapters: context.adapters,
125
+ keepsIngressBody: (tenantId) => payloadCaptureKeepsBodies(context.db, tenantId),
126
+ }),
127
+ createProxyRoutePreparationMiddleware({
128
+ stateStore: context.stateStore,
129
+ preparer: context.preparer,
130
+ }),
131
+ ];
132
+ const mountRoot = (app: Elysia<any, any, any, any, any, any, any, any>): void => {
133
+ app.use(
134
+ createRequestContextMiddleware({
135
+ stateStore: context.stateStore,
136
+ ...(context.requestDeadlineMs === undefined
137
+ ? {}
138
+ : { requestDeadlineMs: context.requestDeadlineMs }),
139
+ }),
140
+ );
141
+ // Root-mounted, not a gateway stage: its `request` hook must run for every
142
+ // `/v1/*` request, including paths that match no route, or a caller can
143
+ // hammer the gateway without ever being counted. See the middleware's own
144
+ // doc for the measured bypass this closes.
145
+ if (context.ipAbuseProtection) {
146
+ app.use(
147
+ createIpAbuseProtectionMiddleware({
148
+ stateStore: context.stateStore,
149
+ ipAbuseProtection: context.ipAbuseProtection,
150
+ trustedProxyBoundary: context.trustedProxyBoundary,
151
+ ...(context.resolvePeerAddress ? { resolvePeerAddress: context.resolvePeerAddress } : {}),
152
+ }),
153
+ );
154
+ }
155
+ app.use(
156
+ createErrorNormalizationMiddleware({
157
+ stateStore: context.stateStore,
158
+ hsts: context.verifiedHttps === true,
159
+ }),
160
+ );
161
+ // Finalize + cleanup live at the ROOT, not on the gateway plugin.
162
+ //
163
+ // A plugin-scoped `afterResponse` only fires for a request that matched a
164
+ // registered route, so an unregistered `/v1/*` path — the cheapest thing a
165
+ // scanner can send — was admitted by the root `request` hook (which does
166
+ // run for every inbound request, that is why the in-flight count and the
167
+ // IP-abuse counter are there) but never cleaned up. Measured: one
168
+ // unmatched `/v1/*` request left `proxy_in_flight` permanently higher and
169
+ // `activeCount()` permanently one larger, so the gauge only ever climbed
170
+ // for the life of the process. The root hook runs for the unmatched path
171
+ // too, which is what makes the increment and the decrement symmetric.
172
+ //
173
+ // Ordering is safe because a root `afterResponse` still runs *after* the
174
+ // matched handler returns (measured: request -> handler -> root.afterResponse
175
+ // -> plugin.afterResponse), so a dispatch route is finalized exactly as
176
+ // before. Registering it only here — never also on the gateway — is what
177
+ // keeps a matched route from finalizing twice.
178
+ // `mountRoot` receives a fully-widened `Elysia<any, ...>`, whose overloaded
179
+ // `afterResponse` signature is not structurally assignable to the minimal
180
+ // `AfterResponseApp` hook interface even though the runtime shape matches
181
+ // (Elysia passes `{ request }`). The cast is confined to this one call.
182
+ const rootApp = app as unknown as AfterResponseApp;
183
+ if (context.telemetry) {
184
+ registerTelemetryLifecycle(rootApp, {
185
+ stateStore: context.stateStore,
186
+ telemetryBuffer: context.telemetry,
187
+ // One capture for the process, shared with every request state so an
188
+ // early rejection can record its payload from the finalize hook. It
189
+ // resolves the tenant's retention itself because an early rejection has
190
+ // no preference memo on its state — nothing resolved one before the
191
+ // request was refused.
192
+ payloadCapture: new TelemetryPayloadCapture(context.db, (tenantId) =>
193
+ resolvePayloadCapture(context.db, tenantId).then(
194
+ (resolved) => (resolved.ok ? resolved.retentionMs : payloadRetentionMs()),
195
+ ),
196
+ ),
197
+ // The early-rejection capture resolves the tenant's capture mode
198
+ // through the same preference path dispatched requests use, instead of
199
+ // capturing bodies under the deployment default regardless of the
200
+ // tenant's none/metadata choice.
201
+ db: context.db,
202
+ });
203
+ } else {
204
+ registerRequestCleanup(rootApp, { stateStore: context.stateStore });
205
+ }
206
+ };
207
+ const createGateway = (
208
+ registerRoutes: (routes: Elysia<any, any, any, any, any, any, any, any>) => void,
209
+ ): Elysia<any, any, any, any, any, any, any, any> => {
210
+ const gateway = new Elysia({ prefix: "/v1" });
211
+ for (const plugin of stages) gateway.use(plugin);
212
+ // No telemetry/cleanup registration here: the lifecycle is owned by
213
+ // `mountRoot`. Registering a second copy on this plugin would finalize a
214
+ // matched request twice, and relying on this copy alone would leak every
215
+ // unmatched `/v1/*` request (see `mountRoot`).
216
+ registerRoutes(gateway);
217
+ return gateway;
218
+ };
219
+ return { mountRoot, createGateway };
220
+ }