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.
- package/.env.example +107 -106
- package/README.md +25 -2
- package/dist/dashboard/assets/{BannedPage-P76QLGJA.js → BannedPage-XaUmiBrC.js} +1 -1
- package/dist/dashboard/assets/BannedPage-XaUmiBrC.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolDetailPage-DdBggJS9.js → CliToolDetailPage-CqD7QiLM.js} +1 -1
- package/dist/dashboard/assets/CliToolDetailPage-CqD7QiLM.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolsPage-aN1iXhhP.js → CliToolsPage-0XUo4UMn.js} +1 -1
- package/dist/dashboard/assets/CliToolsPage-0XUo4UMn.js.gz +0 -0
- package/dist/dashboard/assets/{CombosPage-DE-F1nxZ.js → CombosPage-CXGS_Ct-.js} +1 -1
- package/dist/dashboard/assets/CombosPage-CXGS_Ct-.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-DGnGNYnM.js +1 -0
- package/dist/dashboard/assets/ConsoleLogPage-DGnGNYnM.js.gz +0 -0
- package/dist/dashboard/assets/{CustomizationPage-3CUxMy2X.js → CustomizationPage-DAG5J9EY.js} +1 -1
- package/dist/dashboard/assets/CustomizationPage-DAG5J9EY.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-BKtAPYVF.js +1 -0
- package/dist/dashboard/assets/HealthEventsModal-BKtAPYVF.js.gz +0 -0
- package/dist/dashboard/assets/LoginPage-BdmRLnq2.js +1 -0
- package/dist/dashboard/assets/LoginPage-BdmRLnq2.js.gz +0 -0
- package/dist/dashboard/assets/{ModelPicker-DKVslqG3.js → ModelPicker-BH8Tehdi.js} +1 -1
- package/dist/dashboard/assets/ModelPicker-BH8Tehdi.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-CqMxibJB.js +1 -0
- package/dist/dashboard/assets/OverviewPage-CqMxibJB.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-CqMh2NRX.js +4 -0
- package/dist/dashboard/assets/ProviderDetailPage-CqMh2NRX.js.gz +0 -0
- package/dist/dashboard/assets/ProviderIcon-FB6yS4v1.js +1 -0
- package/dist/dashboard/assets/ProviderIcon-FB6yS4v1.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-B9uWu4Nz.js +1 -0
- package/dist/dashboard/assets/ProvidersPage-B9uWu4Nz.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-BoWOhpDO.js +9 -0
- package/dist/dashboard/assets/ProxyPage-BoWOhpDO.js.gz +0 -0
- package/dist/dashboard/assets/{QuotaPage-BjldQwIU.js → QuotaPage-7P2n--Fv.js} +1 -1
- package/dist/dashboard/assets/QuotaPage-7P2n--Fv.js.gz +0 -0
- package/dist/dashboard/assets/{SettingsPage-DUIoLRxk.js → SettingsPage-CxZ1fTkN.js} +1 -1
- package/dist/dashboard/assets/SettingsPage-CxZ1fTkN.js.gz +0 -0
- package/dist/dashboard/assets/SetupPage-Co12Zzaw.js +1 -0
- package/dist/dashboard/assets/SetupPage-Co12Zzaw.js.gz +0 -0
- package/dist/dashboard/assets/{SortableList-fI7JkdIB.js → SortableList-Dqf4zpS-.js} +1 -1
- package/dist/dashboard/assets/SortableList-Dqf4zpS-.js.gz +0 -0
- package/dist/dashboard/assets/{StudioPage-CHVZqskZ.js → StudioPage-Byj2lAEm.js} +3 -3
- package/dist/dashboard/assets/StudioPage-Byj2lAEm.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-CTkLAuRc.js +56 -0
- package/dist/dashboard/assets/UsagePage-CTkLAuRc.js.gz +0 -0
- package/dist/dashboard/assets/{api-keys-DFY_fZU4.js → api-keys-CUi6UXsR.js} +1 -1
- package/dist/dashboard/assets/api-keys-CUi6UXsR.js.gz +0 -0
- package/dist/dashboard/assets/app-KvsUJUMF.css +2 -0
- package/dist/dashboard/assets/app-KvsUJUMF.css.gz +0 -0
- package/dist/dashboard/assets/{assert-CwdnZARt.js → assert-67xXH5rf.js} +1 -1
- package/dist/dashboard/assets/assert-67xXH5rf.js.gz +0 -0
- package/dist/dashboard/assets/{badge-Dr-uf0Au.js → badge-DC2L43Mj.js} +1 -1
- package/dist/dashboard/assets/badge-DC2L43Mj.js.gz +0 -0
- package/dist/dashboard/assets/{banner-BddmMdoS.js → banner-BBVcb0J3.js} +1 -1
- package/dist/dashboard/assets/banner-BBVcb0J3.js.gz +0 -0
- package/dist/dashboard/assets/{card-CK0PXWlX.js → card-Cca1XC6G.js} +1 -1
- package/dist/dashboard/assets/card-Cca1XC6G.js.gz +0 -0
- package/dist/dashboard/assets/{cli-tools-CcB6mGh2.js → cli-tools-KvsWa1Jl.js} +1 -1
- package/dist/dashboard/assets/cli-tools-KvsWa1Jl.js.gz +0 -0
- package/dist/dashboard/assets/{clipboard-button-DGb2P9Yx.js → clipboard-button-DUzEbynq.js} +1 -1
- package/dist/dashboard/assets/clipboard-button-DUzEbynq.js.gz +0 -0
- package/dist/dashboard/assets/{dialog-LkfuL50u.js → dialog-DFvWhwTa.js} +1 -1
- package/dist/dashboard/assets/dialog-DFvWhwTa.js.gz +0 -0
- package/dist/dashboard/assets/{drawer-ZbEZ_84c.js → drawer-CFM2sMX1.js} +1 -1
- package/dist/dashboard/assets/drawer-CFM2sMX1.js.gz +0 -0
- package/dist/dashboard/assets/{entry-BJfsVMvA.js → entry-Bqy8RFvM.js} +1 -1
- package/dist/dashboard/assets/entry-Bqy8RFvM.js.gz +0 -0
- package/dist/dashboard/assets/{entry-CWBY5A0f.js → entry-ClCMCWWQ.js} +3 -3
- package/dist/dashboard/assets/entry-ClCMCWWQ.js.gz +0 -0
- package/dist/dashboard/assets/{entry-CvC1-Zqx.js → entry-_xHo5x6r.js} +1 -1
- package/dist/dashboard/assets/entry-_xHo5x6r.js.gz +0 -0
- package/dist/dashboard/assets/formatters-CWWZ-uCZ.js +1 -0
- package/dist/dashboard/assets/formatters-CWWZ-uCZ.js.gz +0 -0
- package/dist/dashboard/assets/{github-badge-BxpznFLt.js → github-badge-C9e8Ts7e.js} +1 -1
- package/dist/dashboard/assets/github-badge-C9e8Ts7e.js.gz +0 -0
- package/dist/dashboard/assets/{index-CDzP3ovJ.js → index-DFVFrnWW.js} +2 -2
- package/dist/dashboard/assets/index-DFVFrnWW.js.gz +0 -0
- package/dist/dashboard/assets/{inline-Ae13MQ5w.js → inline-BFUcvllj.js} +1 -1
- package/dist/dashboard/assets/inline-BFUcvllj.js.gz +0 -0
- package/dist/dashboard/assets/{input-D23fo7cp.js → input-C_ILAvBb.js} +1 -1
- package/dist/dashboard/assets/input-C_ILAvBb.js.gz +0 -0
- package/dist/dashboard/assets/{layout-CSsxjj5P.js → layout-CBIyT6Rt.js} +1 -1
- package/dist/dashboard/assets/layout-CBIyT6Rt.js.gz +0 -0
- package/dist/dashboard/assets/{list-row-dmbwZMDY.js → list-row-ebJ_-Nsb.js} +1 -1
- package/dist/dashboard/assets/list-row-ebJ_-Nsb.js.gz +0 -0
- package/dist/dashboard/assets/{live-BG1oUsZZ.js → live-D0mm--ME.js} +1 -1
- package/dist/dashboard/assets/live-D0mm--ME.js.gz +0 -0
- package/dist/dashboard/assets/{mutation-DSF-e4ho.js → mutation-sMD965oO.js} +1 -1
- package/dist/dashboard/assets/mutation-sMD965oO.js.gz +0 -0
- package/dist/dashboard/assets/{numbers-Bm_aECY7.js → numbers-BO9dKaFr.js} +1 -1
- package/dist/dashboard/assets/numbers-BO9dKaFr.js.gz +0 -0
- package/dist/dashboard/assets/providers/inception.svg +5 -0
- package/dist/dashboard/assets/providers/inception.svg.gz +0 -0
- package/dist/dashboard/assets/{query-keys-CF56nYIV.js → query-keys-CfL_Zdoh.js} +1 -1
- package/dist/dashboard/assets/query-keys-CfL_Zdoh.js.gz +0 -0
- package/dist/dashboard/assets/{routing-CeA71n7G.js → routing-BnIpi1IJ.js} +1 -1
- package/dist/dashboard/assets/routing-BnIpi1IJ.js.gz +0 -0
- package/dist/dashboard/assets/{select-KIT0rC6G.js → select-DBNVLdUu.js} +1 -1
- package/dist/dashboard/assets/select-DBNVLdUu.js.gz +0 -0
- package/dist/dashboard/assets/{settings-B_2gkePf.js → settings-BAFhDgX8.js} +1 -1
- package/dist/dashboard/assets/settings-BAFhDgX8.js.gz +0 -0
- package/dist/dashboard/assets/{stack-D7WOSzcX.js → stack-EMrOMt4I.js} +1 -1
- package/dist/dashboard/assets/stack-EMrOMt4I.js.gz +0 -0
- package/dist/dashboard/assets/{state-BBxtKZOQ.js → state-CWoo7ty5.js} +1 -1
- package/dist/dashboard/assets/state-CWoo7ty5.js.gz +0 -0
- package/dist/dashboard/assets/{switch-DNS_D5Ea.js → switch-CCjT6IkF.js} +1 -1
- package/dist/dashboard/assets/switch-CCjT6IkF.js.gz +0 -0
- package/dist/dashboard/assets/{useMutation-BgoxdoMk.js → useMutation-DjkLHUcE.js} +1 -1
- package/dist/dashboard/assets/useMutation-DjkLHUcE.js.gz +0 -0
- package/dist/dashboard/index.html +1 -1
- package/dist/dashboard/index.html.gz +0 -0
- package/dist/dashboard/precompressed-assets.json +54 -53
- package/migrations/full-postgres/0024_add_pool_provider_cooldowns.sql +12 -0
- package/migrations/full-postgres/0025_add_pool_random_strategy.sql +3 -0
- package/migrations/lite-sqlite3/0024_add_pool_provider_cooldowns.sql +12 -0
- package/migrations/lite-sqlite3/0025_add_pool_random_strategy.sql +20 -0
- package/package.json +96 -96
- package/scripts/cli/console-menu.ts +5 -2
- package/scripts/cli/tray.ts +21 -6
- package/src/boot/app.ts +684 -714
- package/src/boot/dependencies.ts +65 -62
- package/src/config/cooldowns.ts +16 -0
- package/src/config/providers.ts +13 -1
- package/src/config/resources.ts +70 -70
- package/src/config/security.ts +37 -37
- package/src/config/spec.ts +371 -358
- package/src/console/cli-tools/contracts.ts +10 -4
- package/src/console/console-router.ts +233 -225
- package/src/console/domain-registration.ts +398 -381
- package/src/console/providers/catalog/catalog-projections.ts +2 -6
- package/src/console/providers/catalog/contracts.ts +19 -0
- package/src/console/providers/catalog/provider-operations.ts +38 -0
- package/src/console/providers/catalog/routes.ts +14 -0
- package/src/console/providers/catalog/store-helpers.ts +326 -0
- package/src/console/providers/catalog/store.ts +1292 -1563
- package/src/console/quota/account-quota-global.ts +2 -2
- package/src/console/quota/account-quota-shared.ts +1 -1
- package/src/console/quota/account-quota-tenant.ts +35 -12
- package/src/console/quota/quota-view.ts +2 -2
- package/src/console/routing/pools/contracts.ts +41 -1
- package/src/console/routing/pools/operations.ts +183 -4
- package/src/console/routing/pools/relay-deploy.ts +17 -4
- package/src/console/routing/pools/routes.ts +30 -0
- package/src/console/routing/pools/store.ts +98 -8
- package/src/main.ts +183 -167
- package/src/network/outbound-fetch.ts +124 -208
- package/src/network/pool/agent.ts +15 -0
- package/src/network/pool/pool-state.ts +7 -0
- package/src/network/pool/selector.ts +110 -2
- package/src/network/pool-cooldown-store.ts +252 -0
- package/src/network/pool-health-machine.ts +5 -3
- package/src/network/pool-health.ts +36 -9
- package/src/observability/event-query.ts +57 -0
- package/src/observability/metrics.ts +467 -467
- package/src/observability/runtime-metrics.ts +269 -269
- package/src/persistence/health-event-flush.ts +2 -0
- package/src/persistence/schema.ts +19 -0
- package/src/persistence/usage-flush.ts +8 -3
- package/src/protocol/canonical-model.ts +1 -1
- package/src/protocol/dsml.ts +377 -0
- package/src/protocol/gateway-error.ts +232 -232
- package/src/{providers/provider-reasoning.ts → protocol/reasoning.ts} +10 -9
- package/src/protocol/request/chat.ts +7 -7
- package/src/protocol/request/codex.ts +2 -2
- package/src/protocol/request/gemini.ts +1 -1
- package/src/protocol/request/messages.ts +6 -6
- package/src/protocol/request/responses.ts +7 -7
- package/src/protocol/response/dsml.ts +12 -133
- package/src/{transport → protocol}/translation/attachment-integrity.ts +2 -2
- package/src/{transport → protocol}/translation/cache-controls.ts +1 -1
- package/src/{transport → protocol}/translation/capabilities.ts +2 -2
- package/src/{transport → protocol}/translation/normalize.ts +1 -1
- package/src/{transport → protocol}/translation/quirks.ts +1 -1
- package/src/{transport → protocol}/translation/search.ts +1 -1
- package/src/{transport → protocol}/translation/thinking.ts +23 -1
- package/src/{transport → protocol}/translation/tool-id.ts +1 -1
- package/src/{transport → protocol}/translation/tool-repair.ts +2 -2
- package/src/protocol/upstream-parsing.ts +176 -0
- package/src/providers/adapters/compatible-adapter.ts +11 -2
- package/src/providers/authentication/oauth-refresh-service.ts +9 -5
- package/src/providers/default-registry.ts +22 -5
- package/src/providers/discovery/probe-phases.ts +16 -3
- package/src/providers/discovery/probing-service.ts +4 -1
- package/src/providers/integrations/anthropic.ts +1 -1
- package/src/providers/integrations/buddy/buddy-catalog-shared.ts +1 -4
- package/src/providers/integrations/buddy/buddy-chat-shared.ts +141 -8
- package/src/providers/integrations/buddy/buddy-checkin.ts +5 -1
- package/src/providers/integrations/buddy/buddy-growth-shared.ts +0 -82
- package/src/providers/integrations/buddy/buddy-identity.ts +25 -0
- package/src/providers/integrations/buddy/buddy-server-tools.ts +240 -0
- package/src/providers/integrations/buddy/codebuddy/cn-catalog.ts +36 -0
- package/src/providers/integrations/buddy/codebuddy/cn-provider.ts +6 -31
- package/src/providers/integrations/buddy/codebuddy/provider.ts +2 -0
- package/src/providers/integrations/buddy/codebuddy/shared.ts +19 -4
- package/src/providers/integrations/buddy/turing-device-token.ts +262 -0
- package/src/providers/integrations/buddy/workbuddy/provider.ts +2 -0
- package/src/providers/integrations/buddy/workbuddy/shared.ts +56 -3
- package/src/providers/integrations/claude/claude.ts +1 -1
- package/src/providers/integrations/cloudflareai/cloudflareai.ts +1 -1
- package/src/providers/integrations/codex/codex-errors.ts +1 -1
- package/src/providers/integrations/codex/codex.ts +1 -1
- package/src/providers/integrations/factory/factory.ts +1 -1
- package/src/providers/integrations/github/github.ts +1 -1
- package/src/providers/integrations/grok/grok.ts +1 -1
- package/src/providers/integrations/inception/provider.ts +91 -0
- package/src/providers/integrations/inferhub.ts +1 -1
- package/src/providers/integrations/kiro/kiro-request.ts +1 -1
- package/src/providers/integrations/mistral.ts +193 -19
- package/src/providers/integrations/ollamacloud-models.ts +50 -0
- package/src/providers/integrations/opencode/fingerprint.ts +19 -2
- package/src/providers/integrations/opencode/prompt.ts +111 -0
- package/src/providers/integrations/opencode/provider.ts +101 -23
- package/src/providers/integrations/opencode/tool-definitions.ts +4 -4
- package/src/providers/integrations/qoder/qoder-checkin.ts +303 -0
- package/src/providers/integrations/qoder/qoder-oauth.ts +12 -1
- package/src/providers/integrations/qoder/qoder-realm.ts +20 -0
- package/src/providers/integrations/qoder/qoder-text-tools.ts +277 -0
- package/src/providers/integrations/qoder/qoder.ts +315 -400
- package/src/providers/integrations/shared/install-id.ts +2 -1
- package/src/providers/integrations/trae/trae-cache.ts +110 -0
- package/src/providers/integrations/trae/trae-oauth.ts +29 -4
- package/src/providers/integrations/trae/trae-protocol.ts +2 -1
- package/src/providers/integrations/trae/trae.ts +106 -23
- package/src/providers/operations/account-error-classification.ts +1684 -0
- package/src/providers/operations/account-health-service.ts +27 -1503
- package/src/providers/operations/account-reset-service.ts +2 -0
- package/src/providers/operations/provider-catalog-service.ts +11 -1
- package/src/providers/provider-contracts.ts +7 -4
- package/src/providers/provider-metadata.ts +25 -4
- package/src/providers/provider-probe.ts +2 -2
- package/src/{console → providers}/quota/cache.ts +1 -1
- package/src/{console → providers}/quota/refresh.ts +5 -5
- package/src/security/cli-client-fingerprint.ts +9 -2
- package/src/transport/dispatch/attempt-finalize.ts +114 -12
- package/src/transport/dispatch/attempt-loop.ts +3 -0
- package/src/transport/dispatch/empty-stream.ts +9 -2
- package/src/transport/dispatch/generation.ts +37 -55
- package/src/transport/dispatch/handler-deps.ts +32 -0
- package/src/transport/dispatch/leases.ts +9 -4
- package/src/transport/dispatch/proxy-request.ts +6 -30
- package/src/transport/dispatch/responses-compact.ts +22 -43
- package/src/transport/dispatch/streaming/finalize.ts +3 -0
- package/src/transport/dispatch/streaming/stream.ts +0 -8
- package/src/transport/dispatch/systemone.ts +22 -42
- package/src/transport/dispatch/token-count.ts +23 -39
- package/src/transport/dispatch/upstream.ts +1 -3
- package/src/transport/dispatch/websearch.ts +22 -41
- package/src/transport/failure-policy.ts +92 -178
- package/src/transport/guards/abuse-guard.ts +10 -0
- package/src/transport/guards/auth-guard.ts +109 -109
- package/src/transport/guards/error-lifecycle.ts +1 -1
- package/src/transport/guards/pipeline.ts +220 -220
- package/src/transport/guards/request-context.ts +245 -238
- package/src/transport/request/degradation.ts +20 -1
- package/src/transport/request/inflight.ts +4 -114
- package/src/transport/request/preparer.ts +6 -6
- package/src/transport/request/state.ts +66 -38
- package/src/transport/request/tenant-preferences.ts +1 -1
- package/src/transport/routing/route-catalog.ts +75 -43
- package/src/transport/routing/router.ts +30 -2
- package/src/transport/surface/messages/parse.ts +1 -1
- package/src/transport/surface/responses/encode.ts +2 -3
- package/src/transport/surface/responses/parse.ts +49 -7
- package/src/types/undici.d.ts +33 -0
- package/src/workers/buddy-growth-tasks.ts +346 -425
- package/src/workers/daily-checkin.ts +17 -6
- package/src/workers/quota-refresh-sweep.ts +2 -2
- package/src/workers/scheduler.ts +17 -1
- package/dist/dashboard/assets/BannedPage-P76QLGJA.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-DdBggJS9.js.gz +0 -0
- package/dist/dashboard/assets/CliToolsPage-aN1iXhhP.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-DE-F1nxZ.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-5SpmfLyw.js +0 -1
- package/dist/dashboard/assets/ConsoleLogPage-5SpmfLyw.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-3CUxMy2X.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-BkmJQKC0.js +0 -1
- package/dist/dashboard/assets/HealthEventsModal-BkmJQKC0.js.gz +0 -0
- package/dist/dashboard/assets/LoginPage-B_0FUKWc.js +0 -1
- package/dist/dashboard/assets/LoginPage-B_0FUKWc.js.gz +0 -0
- package/dist/dashboard/assets/ModelPicker-DKVslqG3.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-D-H4MEiT.js +0 -1
- package/dist/dashboard/assets/OverviewPage-D-H4MEiT.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-CEJw6Guo.js +0 -3
- package/dist/dashboard/assets/ProviderDetailPage-CEJw6Guo.js.gz +0 -0
- package/dist/dashboard/assets/ProviderIcon-CmfzxpVM.js +0 -1
- package/dist/dashboard/assets/ProviderIcon-CmfzxpVM.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-CEplAR-i.js +0 -1
- package/dist/dashboard/assets/ProvidersPage-CEplAR-i.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-CAN7hy0f.js +0 -7
- package/dist/dashboard/assets/ProxyPage-CAN7hy0f.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-BjldQwIU.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-DUIoLRxk.js.gz +0 -0
- package/dist/dashboard/assets/SetupPage-_bCaTM0v.js +0 -1
- package/dist/dashboard/assets/SetupPage-_bCaTM0v.js.gz +0 -0
- package/dist/dashboard/assets/SortableList-fI7JkdIB.js.gz +0 -0
- package/dist/dashboard/assets/StudioPage-CHVZqskZ.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-DiFBymHj.js +0 -56
- package/dist/dashboard/assets/UsagePage-DiFBymHj.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-DFY_fZU4.js.gz +0 -0
- package/dist/dashboard/assets/app-BOJ1_xUB.css +0 -2
- package/dist/dashboard/assets/app-BOJ1_xUB.css.gz +0 -0
- package/dist/dashboard/assets/assert-CwdnZARt.js.gz +0 -0
- package/dist/dashboard/assets/badge-Dr-uf0Au.js.gz +0 -0
- package/dist/dashboard/assets/banner-BddmMdoS.js.gz +0 -0
- package/dist/dashboard/assets/card-CK0PXWlX.js.gz +0 -0
- package/dist/dashboard/assets/cli-tools-CcB6mGh2.js.gz +0 -0
- package/dist/dashboard/assets/clipboard-button-DGb2P9Yx.js.gz +0 -0
- package/dist/dashboard/assets/cog-CdWhsajs.js +0 -1
- package/dist/dashboard/assets/cog-CdWhsajs.js.gz +0 -0
- package/dist/dashboard/assets/dialog-LkfuL50u.js.gz +0 -0
- package/dist/dashboard/assets/drawer-ZbEZ_84c.js.gz +0 -0
- package/dist/dashboard/assets/entry-BJfsVMvA.js.gz +0 -0
- package/dist/dashboard/assets/entry-CWBY5A0f.js.gz +0 -0
- package/dist/dashboard/assets/entry-CvC1-Zqx.js.gz +0 -0
- package/dist/dashboard/assets/github-badge-BxpznFLt.js.gz +0 -0
- package/dist/dashboard/assets/index-CDzP3ovJ.js.gz +0 -0
- package/dist/dashboard/assets/inline-Ae13MQ5w.js.gz +0 -0
- package/dist/dashboard/assets/input-D23fo7cp.js.gz +0 -0
- package/dist/dashboard/assets/layout-CSsxjj5P.js.gz +0 -0
- package/dist/dashboard/assets/list-row-dmbwZMDY.js.gz +0 -0
- package/dist/dashboard/assets/live-BG1oUsZZ.js.gz +0 -0
- package/dist/dashboard/assets/mutation-DSF-e4ho.js.gz +0 -0
- package/dist/dashboard/assets/numbers-Bm_aECY7.js.gz +0 -0
- package/dist/dashboard/assets/query-keys-CF56nYIV.js.gz +0 -0
- package/dist/dashboard/assets/routing-CeA71n7G.js.gz +0 -0
- package/dist/dashboard/assets/select-KIT0rC6G.js.gz +0 -0
- package/dist/dashboard/assets/settings-B_2gkePf.js.gz +0 -0
- package/dist/dashboard/assets/stack-D7WOSzcX.js.gz +0 -0
- package/dist/dashboard/assets/state-BBxtKZOQ.js.gz +0 -0
- package/dist/dashboard/assets/switch-DNS_D5Ea.js.gz +0 -0
- package/dist/dashboard/assets/useMutation-BgoxdoMk.js.gz +0 -0
- /package/dist/dashboard/assets/{app-CaXZ9SCn.js → app-BaWd0puM.js} +0 -0
- /package/dist/dashboard/assets/{app-CaXZ9SCn.js.gz → app-BaWd0puM.js.gz} +0 -0
|
@@ -1,238 +1,245 @@
|
|
|
1
|
-
// Per-request context, client identity, canonical parse, and route preparation.
|
|
2
|
-
import { Elysia } from "elysia";
|
|
3
|
-
import { GatewayError } from "../../protocol/gateway-error";
|
|
4
|
-
import type { TrustedProxyBoundary } from "../../config/network";
|
|
5
|
-
import { resolveUpstreamTimeoutMs } from "../../config/timeouts";
|
|
6
|
-
import type { ProxyRequestStateStore } from "../request/state";
|
|
7
|
-
import { fastPathname } from "../pathname";
|
|
8
|
-
import type { ProxyRequestPreparer } from "../request/preparer";
|
|
9
|
-
import type { CanonicalAdapter } from "../contracts";
|
|
10
|
-
import type { SurfaceAdapterRegistry } from "../surface/adapters";
|
|
11
|
-
import { resolveClientIdentity } from "../../security/ip-boundary";
|
|
12
|
-
import { GATEWAY_SECURITY_HEADERS } from "../../security/outbound-headers";
|
|
13
|
-
import { pushStructuredConsoleLog } from "../../observability/log-ring";
|
|
14
|
-
import { log } from "../../observability/logger";
|
|
15
|
-
import { isJsonProxyRoutePath, isProxyDispatchRoute } from "./body-policy";
|
|
16
|
-
import { isOpaqueBodyRoute } from "../contracts";
|
|
17
|
-
import { peerAddressUnavailableError } from "./guard-errors";
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
interface RequestApp {
|
|
22
|
-
request(
|
|
23
|
-
handler: (context: { request: Request; set?: { headers: Record<string, string> } }) => void,
|
|
24
|
-
): RequestApp;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export interface RequestContextDeps {
|
|
28
|
-
readonly stateStore: ProxyRequestStateStore;
|
|
29
|
-
readonly clock?: () => number;
|
|
30
|
-
/** Explicit deadline override; defaults to `CARTETHYIA_UPSTREAM_TIMEOUT_MS` (120s). */
|
|
31
|
-
readonly requestDeadlineMs?: number;
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
)
|
|
60
|
-
|
|
61
|
-
set.headers
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
readonly
|
|
70
|
-
readonly
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
//
|
|
74
|
-
//
|
|
75
|
-
//
|
|
76
|
-
// `
|
|
77
|
-
//
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
//
|
|
149
|
-
//
|
|
150
|
-
//
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
//
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
if (
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
})
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
}
|
|
1
|
+
// Per-request context, client identity, canonical parse, and route preparation.
|
|
2
|
+
import { Elysia } from "elysia";
|
|
3
|
+
import { GatewayError } from "../../protocol/gateway-error";
|
|
4
|
+
import type { TrustedProxyBoundary } from "../../config/network";
|
|
5
|
+
import { resolveUpstreamTimeoutMs } from "../../config/timeouts";
|
|
6
|
+
import type { ProxyRequestStateStore } from "../request/state";
|
|
7
|
+
import { fastPathname } from "../pathname";
|
|
8
|
+
import type { ProxyRequestPreparer } from "../request/preparer";
|
|
9
|
+
import type { CanonicalAdapter } from "../contracts";
|
|
10
|
+
import type { SurfaceAdapterRegistry } from "../surface/adapters";
|
|
11
|
+
import { resolveClientIdentity } from "../../security/ip-boundary";
|
|
12
|
+
import { GATEWAY_SECURITY_HEADERS } from "../../security/outbound-headers";
|
|
13
|
+
import { pushStructuredConsoleLog } from "../../observability/log-ring";
|
|
14
|
+
import { log } from "../../observability/logger";
|
|
15
|
+
import { isJsonProxyRoutePath, isProxyDispatchRoute } from "./body-policy";
|
|
16
|
+
import { isOpaqueBodyRoute } from "../contracts";
|
|
17
|
+
import { peerAddressUnavailableError } from "./guard-errors";
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
interface RequestApp {
|
|
22
|
+
request(
|
|
23
|
+
handler: (context: { request: Request; set?: { headers: Record<string, string> } }) => void,
|
|
24
|
+
): RequestApp;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface RequestContextDeps {
|
|
28
|
+
readonly stateStore: ProxyRequestStateStore;
|
|
29
|
+
readonly clock?: () => number;
|
|
30
|
+
/** Explicit deadline override; defaults to `CARTETHYIA_UPSTREAM_TIMEOUT_MS` (120s). */
|
|
31
|
+
readonly requestDeadlineMs?: number;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Initializes per-request `ProxyRequestState` before any other hook runs.
|
|
36
|
+
* Only gateway (`/v1/*`) requests get state: this middleware is mounted at
|
|
37
|
+
* the composition root so it also observes health checks, console traffic,
|
|
38
|
+
* and dashboard assets, but none of those use proxy state and none of them
|
|
39
|
+
* run the `/v1`-scoped cleanup hooks — initializing for them leaked a state,
|
|
40
|
+
* a deadline timer, a live-controller entry, and an in-flight count per
|
|
41
|
+
* request that was never reclaimed. Cleanup (abort + WeakMap eviction) is
|
|
42
|
+
* intentionally NOT done here via `afterResponse`: Elysia's afterResponse
|
|
43
|
+
* hook order across plugins is not something this composition can guarantee
|
|
44
|
+
* ahead of `createTelemetryLifecycleMiddleware`'s afterResponse (now in
|
|
45
|
+
* lifecycle.ts), which still needs to read the state. The WeakMap entry is
|
|
46
|
+
* reclaimed by GC once `request` is unreferenced; nothing needs to run after
|
|
47
|
+
* the response is sent.
|
|
48
|
+
*/
|
|
49
|
+
export function createRequestContextMiddleware(deps: RequestContextDeps): Elysia {
|
|
50
|
+
const app = new Elysia() as unknown as RequestApp;
|
|
51
|
+
app.request(({ request, set }) => {
|
|
52
|
+
const pathname = fastPathname(request.url);
|
|
53
|
+
if (pathname !== "/v1" && !pathname.startsWith("/v1/")) return;
|
|
54
|
+
const state = deps.stateStore.initialize(
|
|
55
|
+
request,
|
|
56
|
+
(deps.clock ?? (() => Date.now()))(),
|
|
57
|
+
deps.requestDeadlineMs ?? resolveUpstreamTimeoutMs(),
|
|
58
|
+
);
|
|
59
|
+
if (set) {
|
|
60
|
+
set.headers["x-request-id"] = state.requestId;
|
|
61
|
+
Object.assign(set.headers, GATEWAY_SECURITY_HEADERS);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
return app as unknown as Elysia;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function createClientIdentityMiddleware(deps: {
|
|
68
|
+
readonly stateStore: ProxyRequestStateStore;
|
|
69
|
+
readonly trustedProxyBoundary: TrustedProxyBoundary;
|
|
70
|
+
readonly resolvePeerAddress?: (request: Request) => string | null;
|
|
71
|
+
}): Elysia {
|
|
72
|
+
// `beforeHandle`, not `onRequest`: plugin-scoped `onRequest` hooks do not
|
|
73
|
+
// fire for these routes under Elysia 2 beta (same scoping gap that muted
|
|
74
|
+
// plugin `afterResponse` before `registerTelemetryLifecycle`), which left
|
|
75
|
+
// `clientIdentity` unset and every gateway row without a client IP.
|
|
76
|
+
// `beforeHandle` hooks from `.use()` plugins run in registration order, so
|
|
77
|
+
// pipeline position still guarantees this executes before IP-abuse checks.
|
|
78
|
+
return new Elysia()
|
|
79
|
+
.beforeHandle(({ request, server }) => {
|
|
80
|
+
const state = deps.stateStore.require(request);
|
|
81
|
+
// The IP-abuse root hook already resolved the same identity from the
|
|
82
|
+
// same boundary (and only for /v1/*, exactly these routes); reuse it so
|
|
83
|
+
// a request pays one resolveClientIdentity instead of two. Fall back to
|
|
84
|
+
// resolving here when running in a reduced composition without the
|
|
85
|
+
// abuse guard.
|
|
86
|
+
if (!state.clientIdentity) {
|
|
87
|
+
const peer = deps.resolvePeerAddress
|
|
88
|
+
? deps.resolvePeerAddress(request)
|
|
89
|
+
: (server?.requestIP(request)?.address ?? null);
|
|
90
|
+
if (!peer)
|
|
91
|
+
throw peerAddressUnavailableError();
|
|
92
|
+
const address = resolveClientIdentity(request, deps.trustedProxyBoundary, peer);
|
|
93
|
+
state.clientIdentity = {
|
|
94
|
+
address,
|
|
95
|
+
source: address === peer ? "tcp-peer" : "trusted-forwarded-header",
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
const address = state.clientIdentity.address;
|
|
99
|
+
const path = state.ingressPath;
|
|
100
|
+
if (!isProxyDispatchRoute(path)) return;
|
|
101
|
+
// The ingress policy stage already decoded the body, so the client's
|
|
102
|
+
// requested model is readable here — before canonical parsing runs. It is
|
|
103
|
+
// read defensively and only for display: routing still resolves the model
|
|
104
|
+
// through the surface adapter, and a body that is not an object simply
|
|
105
|
+
// yields no model rather than failing the request.
|
|
106
|
+
const body = state.ingressBody;
|
|
107
|
+
const requestedModel =
|
|
108
|
+
body !== null && typeof body === "object" && !Array.isArray(body)
|
|
109
|
+
? (body as Record<string, unknown>)["model"]
|
|
110
|
+
: undefined;
|
|
111
|
+
const userAgent = request.headers.get("user-agent");
|
|
112
|
+
pushStructuredConsoleLog("info", "Incoming proxy request", {
|
|
113
|
+
event: "request_start",
|
|
114
|
+
requestId: state.requestId,
|
|
115
|
+
method: request.method,
|
|
116
|
+
endpoint: path,
|
|
117
|
+
clientIp: address,
|
|
118
|
+
...(typeof requestedModel === "string" && requestedModel.length > 0
|
|
119
|
+
? { model: requestedModel }
|
|
120
|
+
: {}),
|
|
121
|
+
...(userAgent === null || userAgent.length === 0
|
|
122
|
+
? {}
|
|
123
|
+
: { userAgent: userAgent.slice(0, 512) }),
|
|
124
|
+
});
|
|
125
|
+
})
|
|
126
|
+
.as("plugin");
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export function createCanonicalRequestMiddleware(deps: {
|
|
130
|
+
readonly stateStore: ProxyRequestStateStore;
|
|
131
|
+
readonly surfaceRegistry: SurfaceAdapterRegistry;
|
|
132
|
+
readonly adapters: ReadonlyMap<string, CanonicalAdapter>;
|
|
133
|
+
/**
|
|
134
|
+
* Whether this tenant's payload-capture mode keeps request bodies
|
|
135
|
+
* (`full`). `none`/`metadata` never read the request body at finalize, so
|
|
136
|
+
* the parsed ingress body can be released as soon as the canonical request
|
|
137
|
+
* exists instead of being held — across the whole upstream stream. Injected
|
|
138
|
+
* so this stage does not import the capture policy or the database.
|
|
139
|
+
*/
|
|
140
|
+
readonly keepsIngressBody: (tenantId: string | null) => Promise<boolean>;
|
|
141
|
+
}): Elysia {
|
|
142
|
+
return new Elysia()
|
|
143
|
+
.beforeHandle(async ({ request }) => {
|
|
144
|
+
const path = fastPathname(request.url);
|
|
145
|
+
if (!path.startsWith("/v1/")) return;
|
|
146
|
+
if (request.method === "GET" || request.method === "HEAD") return;
|
|
147
|
+
// `/v1/completions` needs no separate arm: it is already in
|
|
148
|
+
// the JSON routes table, so the body-policy predicate covers it.
|
|
149
|
+
// Native routes (compact, System One) read a body but are never parsed
|
|
150
|
+
// into a canonical request — their bodies are opaque by contract.
|
|
151
|
+
if (!isJsonProxyRoutePath(path) || path === "/v1/responses/compact" || isOpaqueBodyRoute(path))
|
|
152
|
+
return;
|
|
153
|
+
// Model discovery and other GET/multimodal routes must not go through canonical parsing.
|
|
154
|
+
const state = deps.stateStore.require(request);
|
|
155
|
+
// Single-read invariant: the ingress policy middleware already decoded the
|
|
156
|
+
// body once into `state.ingressBody`. Never re-read `request.body` here —
|
|
157
|
+
// it is already consumed/locked. `undefined` means the ingress stage did
|
|
158
|
+
// not run (reduced composition); fail closed. An explicit `null` (JSON
|
|
159
|
+
// literal `null` body) is a valid single-read value and flows to the
|
|
160
|
+
// surface adapter's parse error rather than triggering a second read.
|
|
161
|
+
if (state.ingressBody === undefined)
|
|
162
|
+
throw new GatewayError("admission_unavailable", 503, "The request body was never read, so this route cannot be served. Resend the request.");
|
|
163
|
+
const body = state.ingressBody;
|
|
164
|
+
const headers: Record<string, string> = {};
|
|
165
|
+
request.headers.forEach((value, key) => {
|
|
166
|
+
headers[key] = value;
|
|
167
|
+
});
|
|
168
|
+
const detection = deps.surfaceRegistry.detectOnce({ body, headers, path });
|
|
169
|
+
if (detection.disagreement) {
|
|
170
|
+
// A path-vs-body disagreement is expected traffic, not a fault: a
|
|
171
|
+
// standard chat body ({model, messages[], max_tokens}) also matches
|
|
172
|
+
// the messages body shape, so nearly every chat request with
|
|
173
|
+
// max_tokens "disagrees". Warn only when the explicit header is
|
|
174
|
+
// involved — a marker contradicting the path means the client is
|
|
175
|
+
// genuinely confused about which surface it wants.
|
|
176
|
+
const markerInvolved = detection.signals.some((s) => s.source === "explicit_marker");
|
|
177
|
+
if (markerInvolved) {
|
|
178
|
+
log.warn("[surface] conflicting signals detected for surface routing", {
|
|
179
|
+
resolved: detection.surface,
|
|
180
|
+
winning_source: detection.winning_source,
|
|
181
|
+
signals: detection.signals,
|
|
182
|
+
});
|
|
183
|
+
} else {
|
|
184
|
+
log.debug("[surface] ambiguous body shape resolved by endpoint path", {
|
|
185
|
+
resolved: detection.surface,
|
|
186
|
+
signals: detection.signals,
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
const adapter = deps.adapters.get(detection.surface);
|
|
191
|
+
if (!adapter)
|
|
192
|
+
throw new GatewayError(
|
|
193
|
+
"capability_unsupported",
|
|
194
|
+
400,
|
|
195
|
+
`Surface adapter unavailable: ${detection.surface}`,
|
|
196
|
+
{ surface: detection.surface },
|
|
197
|
+
);
|
|
198
|
+
const ingress = deps.stateStore.require(request);
|
|
199
|
+
const userAgent = headers["user-agent"];
|
|
200
|
+
if (userAgent) ingress.clientUserAgent = userAgent.slice(0, 512);
|
|
201
|
+
ingress.canonicalRequest = adapter.parse({ body, headers, path });
|
|
202
|
+
// The parsed body is the largest per-request value, and once the
|
|
203
|
+
// canonical request exists the only later reader is payload capture — and
|
|
204
|
+
// only in `full` mode (see `captureTerminalPayload`). For `none`/`metadata`
|
|
205
|
+
// drop the reference now: held until cleanup, it would otherwise stay
|
|
206
|
+
// resident for the entire upstream stream.
|
|
207
|
+
if (!(await deps.keepsIngressBody(ingress.authorization?.tenantId ?? null))) {
|
|
208
|
+
ingress.releaseIngressBody();
|
|
209
|
+
}
|
|
210
|
+
})
|
|
211
|
+
.as("plugin");
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export function createProxyRoutePreparationMiddleware(deps: {
|
|
215
|
+
readonly stateStore: ProxyRequestStateStore;
|
|
216
|
+
readonly preparer: ProxyRequestPreparer;
|
|
217
|
+
}): Elysia {
|
|
218
|
+
return new Elysia()
|
|
219
|
+
.beforeHandle(async ({ request }) => {
|
|
220
|
+
const path = fastPathname(request.url);
|
|
221
|
+
if (!path.startsWith("/v1/")) return;
|
|
222
|
+
if (request.method === "GET" || request.method === "HEAD") return;
|
|
223
|
+
const isJsonRoute = isJsonProxyRoutePath(path);
|
|
224
|
+
if (!isJsonRoute || path === "/v1/responses/compact" || isOpaqueBodyRoute(path)) return;
|
|
225
|
+
const state = deps.stateStore.require(request);
|
|
226
|
+
if (!state.authorization || !state.canonicalRequest)
|
|
227
|
+
throw new GatewayError("admission_unavailable", 503, "The request was not authenticated and parsed, so this route cannot be served. Resend the request.");
|
|
228
|
+
// The key rides along so a strike is charged to the caller that made it.
|
|
229
|
+
// Without it, one recipient of a shared link could escalate a ban onto
|
|
230
|
+
try {
|
|
231
|
+
state.preparedRequest = await deps.preparer.prepare({
|
|
232
|
+
canonicalRequest: state.canonicalRequest,
|
|
233
|
+
authorization: state.authorization,
|
|
234
|
+
deadlineMs: state.deadlineMs,
|
|
235
|
+
signal: state.abortController.signal,
|
|
236
|
+
...(state.clientUserAgent === undefined
|
|
237
|
+
? {}
|
|
238
|
+
: { clientUserAgent: state.clientUserAgent }),
|
|
239
|
+
});
|
|
240
|
+
} catch (error) {
|
|
241
|
+
throw error;
|
|
242
|
+
}
|
|
243
|
+
})
|
|
244
|
+
.as("plugin");
|
|
245
|
+
}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* the planner warns (throttled here) when a request was dispatched degraded.
|
|
8
8
|
*/
|
|
9
9
|
import type { CanonicalRequest, ContentPart } from "../../protocol/canonical-model";
|
|
10
|
-
import { deriveRequiredCapabilities, type RequiredCapability } from "
|
|
10
|
+
import { deriveRequiredCapabilities, type RequiredCapability } from "../../protocol/translation/capabilities";
|
|
11
11
|
|
|
12
12
|
// Degradation is often sustained — a client that always asks for tools against
|
|
13
13
|
// a tools-less route degrades every single request. An unthrottled warn would
|
|
@@ -261,6 +261,25 @@ export function* degradedRequestVariants(
|
|
|
261
261
|
"audio",
|
|
262
262
|
] as RequiredCapability[];
|
|
263
263
|
const ordered = priority.filter((c) => required.includes(c));
|
|
264
|
+
// Pre-pass: drop exactly one capability from the pristine request. The
|
|
265
|
+
// cumulative chain below can only ADD losses, so when the plan's real blocker
|
|
266
|
+
// is a late capability in this order (an image on a text-only model), every
|
|
267
|
+
// surviving chain variant has already lost `tools` as collateral — a
|
|
268
|
+
// tool-capable model then served a transcript of `[tool:<name>]` placeholders
|
|
269
|
+
// with no tool catalog at all. Trying each single loss first finds the
|
|
270
|
+
// smallest degradation that satisfies routing: `degraded: [cap]` alone is
|
|
271
|
+
// always lexicographically smaller than the chain's `[...earlier, cap]`, so
|
|
272
|
+
// a single-loss win must be preferred, never the reverse.
|
|
273
|
+
for (const cap of ordered) {
|
|
274
|
+
const singular = degradeRequestForCapability(original, cap);
|
|
275
|
+
if (singular) {
|
|
276
|
+
yield {
|
|
277
|
+
request: singular,
|
|
278
|
+
degraded: [cap],
|
|
279
|
+
required: required.filter((c) => c !== cap),
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
}
|
|
264
283
|
let current: CanonicalRequest = original;
|
|
265
284
|
const degraded: RequiredCapability[] = [];
|
|
266
285
|
for (const cap of ordered) {
|