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,120 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* The registry is *owned* by the `ProxyRequestStateStore` (`state.ts`), which
|
|
8
|
-
* is also the one place that evicts a request, so a flight cannot outlive its
|
|
9
|
-
* state. A separate process-global counter alongside the store's
|
|
10
|
-
* `liveControllers` map would be a second registry able to disagree with it: a
|
|
11
|
-
* flight the store had already torn down would stay counted there forever.
|
|
12
|
-
*
|
|
13
|
-
* The store hands each entry an `expiresAtMs` (the request's hard deadline). A
|
|
14
|
-
* periodic backstop sweep reads `overdue()` and force-releases any flight whose
|
|
15
|
-
* deadline has passed, so the gauge settles even if an abort path never fires.
|
|
16
|
-
*
|
|
17
|
-
* Pub/sub lets the console push the live snapshot over SSE instead of polling.
|
|
18
|
-
* Process-local: each gateway instance reports its own flights.
|
|
2
|
+
* The live "in flight" snapshot shape, shared by the store, the console live
|
|
3
|
+
* domain, and the dashboard. Derived from the request state store's own
|
|
4
|
+
* `liveControllers` map — the single registry that already owns a request's
|
|
5
|
+
* lifetime — so the gauge cannot disagree with what is actually running.
|
|
19
6
|
*/
|
|
20
|
-
import { metrics } from "../../observability/metrics";
|
|
21
|
-
|
|
22
7
|
export interface InFlightSnapshot {
|
|
23
8
|
readonly inFlight: number;
|
|
24
9
|
readonly uniqueIps: number;
|
|
25
|
-
/**
|
|
26
|
-
* How long the longest-lived flight has been tracked, in milliseconds.
|
|
27
|
-
*
|
|
28
|
-
* The count alone cannot tell a healthy queue from a leak: six flights
|
|
29
|
-
* could be six requests in progress or six that never finished. The age
|
|
30
|
-
* separates them — a flight far past its own deadline is the backstop's
|
|
31
|
-
* job, so a growing age with a flat count means the sweep is not running.
|
|
32
|
-
*/
|
|
33
|
-
readonly oldestAgeMs: number;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
interface Flight {
|
|
37
|
-
readonly clientIp: string;
|
|
38
|
-
/** Hard deadline after which the backstop sweep releases this flight. */
|
|
39
|
-
expiresAtMs: number;
|
|
40
|
-
/** When this flight was tracked, for the age reported in a snapshot. */
|
|
41
|
-
readonly startedAtMs: number;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export interface InFlightRegistry {
|
|
45
|
-
/** Register a flight once its dispatch leases are acquired. Idempotent per id. */
|
|
46
|
-
track(requestId: string, clientIp: string, expiresAtMs: number): void;
|
|
47
|
-
/** Release a flight. Idempotent; an unknown id is a no-op. */
|
|
48
|
-
untrack(requestId: string): void;
|
|
49
|
-
/** Re-arm a flight's hard deadline (streaming swaps the pre-stream budget). */
|
|
50
|
-
extend(requestId: string, expiresAtMs: number): void;
|
|
51
|
-
snapshot(): InFlightSnapshot;
|
|
52
|
-
count(): number;
|
|
53
|
-
/** Ids whose deadline plus the sweep grace has elapsed — the backstop's worklist. */
|
|
54
|
-
overdue(nowMs: number, graceMs: number): readonly string[];
|
|
55
|
-
subscribe(listener: (snapshot: InFlightSnapshot) => void): () => void;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export function createInFlightRegistry(): InFlightRegistry {
|
|
59
|
-
const flights = new Map<string, Flight>();
|
|
60
|
-
const listeners = new Set<(snapshot: InFlightSnapshot) => void>();
|
|
61
|
-
|
|
62
|
-
const snapshot = (): InFlightSnapshot => {
|
|
63
|
-
let uniqueIps = 0;
|
|
64
|
-
let oldest = 0;
|
|
65
|
-
const now = Date.now();
|
|
66
|
-
const seen = new Set<string>();
|
|
67
|
-
for (const flight of flights.values()) {
|
|
68
|
-
if (!seen.has(flight.clientIp)) {
|
|
69
|
-
seen.add(flight.clientIp);
|
|
70
|
-
uniqueIps += 1;
|
|
71
|
-
}
|
|
72
|
-
const age = now - flight.startedAtMs;
|
|
73
|
-
if (age > oldest) oldest = age;
|
|
74
|
-
}
|
|
75
|
-
return { inFlight: flights.size, uniqueIps, oldestAgeMs: oldest };
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
const publish = (): void => {
|
|
79
|
-
const current = snapshot();
|
|
80
|
-
metrics.proxy_in_flight.set(current.inFlight);
|
|
81
|
-
for (const listener of listeners) listener(current);
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
return {
|
|
85
|
-
track(requestId, clientIp, expiresAtMs) {
|
|
86
|
-
flights.set(requestId, {
|
|
87
|
-
clientIp: clientIp.length > 0 ? clientIp : "unknown",
|
|
88
|
-
expiresAtMs,
|
|
89
|
-
startedAtMs: Date.now(),
|
|
90
|
-
});
|
|
91
|
-
publish();
|
|
92
|
-
},
|
|
93
|
-
untrack(requestId) {
|
|
94
|
-
if (!flights.delete(requestId)) return;
|
|
95
|
-
publish();
|
|
96
|
-
},
|
|
97
|
-
extend(requestId, expiresAtMs) {
|
|
98
|
-
const flight = flights.get(requestId);
|
|
99
|
-
if (flight === undefined) return;
|
|
100
|
-
flight.expiresAtMs = expiresAtMs;
|
|
101
|
-
},
|
|
102
|
-
snapshot,
|
|
103
|
-
count() {
|
|
104
|
-
return flights.size;
|
|
105
|
-
},
|
|
106
|
-
overdue(nowMs, graceMs) {
|
|
107
|
-
const ids: string[] = [];
|
|
108
|
-
for (const [requestId, flight] of flights) {
|
|
109
|
-
if (nowMs >= flight.expiresAtMs + graceMs) ids.push(requestId);
|
|
110
|
-
}
|
|
111
|
-
return ids;
|
|
112
|
-
},
|
|
113
|
-
subscribe(listener) {
|
|
114
|
-
listeners.add(listener);
|
|
115
|
-
return () => {
|
|
116
|
-
listeners.delete(listener);
|
|
117
|
-
};
|
|
118
|
-
},
|
|
119
|
-
};
|
|
120
10
|
}
|
|
@@ -5,14 +5,14 @@ import type { ApiKeyAdmissionService } from "../../security/admission/service";
|
|
|
5
5
|
import type { RouteCandidate as RouteCandidate, InMemoryRouteSnapshotService, RoutePlan } from "../routing/route-model";
|
|
6
6
|
import type { RouteContinuationAffinity } from "../routing/route-model";
|
|
7
7
|
import { resolveAliasTarget, type RoutingEngine } from "../routing/router";
|
|
8
|
-
import { projectForRoute, routeCapabilitiesFor } from "
|
|
9
|
-
import type { RequiredCapability } from "
|
|
8
|
+
import { projectForRoute, routeCapabilitiesFor } from "../../protocol/translation/capabilities";
|
|
9
|
+
import type { RequiredCapability } from "../../protocol/translation/capabilities";
|
|
10
10
|
import { isModelAllowed, type ResolvedApiKey } from "../../security/api-key-auth";
|
|
11
11
|
import { allowsCliToolMappings } from "../../security/cli-client-fingerprint";
|
|
12
|
-
import { dropIncompleteToolRounds, repairRequestToolCalls } from "
|
|
13
|
-
import { sanitizeRequestToolIds } from "
|
|
14
|
-
import { dropCorruptAttachments } from "
|
|
15
|
-
import { parseThinkingSuffix, withThinkingSuffixIntent } from "
|
|
12
|
+
import { dropIncompleteToolRounds, repairRequestToolCalls } from "../../protocol/translation/tool-repair";
|
|
13
|
+
import { sanitizeRequestToolIds } from "../../protocol/translation/tool-id";
|
|
14
|
+
import { dropCorruptAttachments } from "../../protocol/translation/attachment-integrity";
|
|
15
|
+
import { parseThinkingSuffix, withThinkingSuffixIntent } from "../../protocol/translation/thinking";
|
|
16
16
|
import { nativeServicePathFor } from "../contracts";
|
|
17
17
|
import { log } from "../../observability/logger";
|
|
18
18
|
import { BUDDY_PROVIDER_IDS } from "../../providers/provider-metadata";
|
|
@@ -5,8 +5,8 @@ import type { ResolvedApiKey } from "../../security/api-key-auth";
|
|
|
5
5
|
import type { PreparedProxyRequest } from "./preparer";
|
|
6
6
|
import type { PayloadCaptureState, TelemetryPayloadCapture } from "../../observability/payload-capture";
|
|
7
7
|
import type { TelemetryPayloadDepth, TelemetryPayloadMode } from "../../console/settings/contracts";
|
|
8
|
-
import { createInFlightRegistry, type InFlightSnapshot } from "./inflight";
|
|
9
8
|
import { log } from "../../observability/logger";
|
|
9
|
+
import type { InFlightSnapshot } from "./inflight";
|
|
10
10
|
import { fastPathname } from "../pathname";
|
|
11
11
|
|
|
12
12
|
export interface ProxyRequestOutcome {
|
|
@@ -182,15 +182,39 @@ export interface RequestTracker {
|
|
|
182
182
|
* shutdown coordination, which waits on tracked requests (or aborts them
|
|
183
183
|
* past the grace window) instead of relying on connection close events.
|
|
184
184
|
*/
|
|
185
|
+
/**
|
|
186
|
+
* Minimal pub/sub over a store's snapshot. Polling already re-reads
|
|
187
|
+
* `inFlightSnapshot()`, so this only pushes the same derived value to the
|
|
188
|
+
* console's SSE stream; no second registry behind it.
|
|
189
|
+
*/
|
|
190
|
+
function subscribeInFlightSnapshot(
|
|
191
|
+
store: { inFlightSnapshot(): InFlightSnapshot },
|
|
192
|
+
listener: (snapshot: InFlightSnapshot) => void,
|
|
193
|
+
): () => void {
|
|
194
|
+
const timers = new Set<ReturnType<typeof setTimeout>>();
|
|
195
|
+
const push = (): void => listener(store.inFlightSnapshot());
|
|
196
|
+
push();
|
|
197
|
+
const handle = setInterval(push, 1_000);
|
|
198
|
+
(handle as unknown as { unref?: () => void })?.unref?.();
|
|
199
|
+
return () => {
|
|
200
|
+
clearInterval(handle);
|
|
201
|
+
for (const t of timers) clearTimeout(t);
|
|
202
|
+
timers.clear();
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
|
|
185
206
|
export class ProxyRequestStateStore {
|
|
186
207
|
private readonly states = new WeakMap<Request, ProxyRequestState>();
|
|
187
208
|
private readonly liveControllers = new Map<string, AbortController>();
|
|
188
209
|
/**
|
|
189
|
-
* The in-flight gauge
|
|
190
|
-
*
|
|
191
|
-
*
|
|
210
|
+
* The in-flight gauge is derived from the same map that owns a request's
|
|
211
|
+
* lifetime. There is no second registry: a count that disagreed with
|
|
212
|
+
* `liveControllers` was the ghost — entries that outlived the request they
|
|
213
|
+
* described. The deadline each flight last armed is kept alongside, so the
|
|
214
|
+
* backstop sweep still finds requests whose abort path never fired.
|
|
192
215
|
*/
|
|
193
|
-
private readonly
|
|
216
|
+
private readonly inFlightDeadlines = new Map<string, number>();
|
|
217
|
+
private readonly inFlightIps = new Map<string, string>();
|
|
194
218
|
|
|
195
219
|
constructor(private readonly tracker?: RequestTracker) {}
|
|
196
220
|
|
|
@@ -199,43 +223,38 @@ export class ProxyRequestStateStore {
|
|
|
199
223
|
}
|
|
200
224
|
|
|
201
225
|
inFlightSnapshot(): InFlightSnapshot {
|
|
202
|
-
|
|
226
|
+
let uniqueIps = 0;
|
|
227
|
+
const seen = new Set<string>();
|
|
228
|
+
for (const ip of this.inFlightIps.values()) {
|
|
229
|
+
if (!seen.has(ip)) {
|
|
230
|
+
seen.add(ip);
|
|
231
|
+
uniqueIps += 1;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
return { inFlight: this.inFlightDeadlines.size, uniqueIps };
|
|
203
235
|
}
|
|
204
236
|
|
|
205
237
|
/** Live flight count for this store. */
|
|
206
238
|
inFlightCount(): number {
|
|
207
|
-
return this.
|
|
239
|
+
return this.inFlightDeadlines.size;
|
|
208
240
|
}
|
|
209
241
|
|
|
210
242
|
subscribeInFlight(listener: (snapshot: InFlightSnapshot) => void): () => void {
|
|
211
|
-
return this
|
|
243
|
+
return subscribeInFlightSnapshot(this, listener);
|
|
212
244
|
}
|
|
213
245
|
|
|
214
246
|
/**
|
|
215
|
-
*
|
|
216
|
-
*
|
|
217
|
-
*
|
|
218
|
-
* that neither pulls, cancels, nor aborts
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
/**
|
|
225
|
-
* Releases every flight past its deadline. Two tiers, because aborting the
|
|
226
|
-
* controller unwinds the fetch but only *releases* the flight if some path
|
|
227
|
-
* observes the abort:
|
|
228
|
-
*
|
|
229
|
-
* - Past `expiresAtMs + graceMs`: abort the controller, which unblocks a
|
|
230
|
-
* paused read and fires the release in `dispatch/proxy-request`.
|
|
231
|
-
* - Past `expiresAtMs + hardGraceMs`: the abort did not release it, so
|
|
232
|
-
* force-drop the gauge entry. The fetch is already torn down by the abort,
|
|
233
|
-
* so leaving the entry would only mean a permanently wrong number — the
|
|
234
|
-
* exact defect this backstop exists to end. Logged, never silent.
|
|
247
|
+
* Releases every flight past its deadline. Aborting the controller unwinds
|
|
248
|
+
* the fetch and lets the request's own `cleanup` run, which drops the
|
|
249
|
+
* flight's entry with it. A request nothing observes — a half-closed socket
|
|
250
|
+
* that neither pulls, cancels, nor aborts — never runs that cleanup, so
|
|
251
|
+
* after a further grace window the sweep drops the entry itself: the fetch
|
|
252
|
+
* is already torn down by the abort, and a permanently wrong number is the
|
|
253
|
+
* exact defect this backstop exists to end. Logged, never silent.
|
|
235
254
|
*/
|
|
236
255
|
sweepOverdueInFlight(nowMs: number, graceMs: number, hardGraceMs: number): number {
|
|
237
|
-
const
|
|
238
|
-
|
|
256
|
+
for (const [requestId, deadline] of this.inFlightDeadlines) {
|
|
257
|
+
if (nowMs < deadline + graceMs) continue;
|
|
239
258
|
const controller = this.liveControllers.get(requestId);
|
|
240
259
|
try {
|
|
241
260
|
if (controller && !controller.signal.aborted) {
|
|
@@ -246,14 +265,19 @@ export class ProxyRequestStateStore {
|
|
|
246
265
|
} catch {
|
|
247
266
|
}
|
|
248
267
|
}
|
|
249
|
-
|
|
250
|
-
for (const requestId of
|
|
268
|
+
let forced = 0;
|
|
269
|
+
for (const [requestId, deadline] of this.inFlightDeadlines) {
|
|
270
|
+
if (nowMs < deadline + hardGraceMs) continue;
|
|
271
|
+
this.inFlightDeadlines.delete(requestId);
|
|
272
|
+
this.inFlightIps.delete(requestId);
|
|
273
|
+
forced += 1;
|
|
274
|
+
}
|
|
275
|
+
if (forced > 0) {
|
|
251
276
|
log.error(
|
|
252
|
-
`[inflight-backstop] force-releasing
|
|
277
|
+
`[inflight-backstop] force-releasing ${forced} flight(s): abort did not release them within ${hardGraceMs}ms`,
|
|
253
278
|
);
|
|
254
|
-
this.inFlight.untrack(requestId);
|
|
255
279
|
}
|
|
256
|
-
return
|
|
280
|
+
return forced;
|
|
257
281
|
}
|
|
258
282
|
|
|
259
283
|
abortAll(reason?: unknown): void {
|
|
@@ -325,7 +349,8 @@ export class ProxyRequestStateStore {
|
|
|
325
349
|
startProviderFlight: () => {
|
|
326
350
|
if (cleaned || providerFlightStarted) return;
|
|
327
351
|
providerFlightStarted = true;
|
|
328
|
-
this.
|
|
352
|
+
this.inFlightDeadlines.set(requestId, state.deadlineMs);
|
|
353
|
+
this.inFlightIps.set(requestId, state.clientIdentity?.address || "unknown");
|
|
329
354
|
},
|
|
330
355
|
releaseIngressBody: () => {
|
|
331
356
|
state.ingressBody = undefined;
|
|
@@ -342,7 +367,7 @@ export class ProxyRequestStateStore {
|
|
|
342
367
|
// request that legitimately extends its budget must move the flight's
|
|
343
368
|
// deadline too — otherwise the sweep would force-release a healthy
|
|
344
369
|
// long stream the moment the pre-stream budget elapsed.
|
|
345
|
-
this.
|
|
370
|
+
if (providerFlightStarted) this.inFlightDeadlines.set(requestId, state.deadlineMs);
|
|
346
371
|
armDeadline(ms);
|
|
347
372
|
},
|
|
348
373
|
cleanup: () => {
|
|
@@ -351,7 +376,10 @@ export class ProxyRequestStateStore {
|
|
|
351
376
|
// Every step is guarded: one throwing cleanup must not skip the
|
|
352
377
|
// remaining cleanups, the abort, or the map eviction (leak).
|
|
353
378
|
try {
|
|
354
|
-
if (providerFlightStarted)
|
|
379
|
+
if (providerFlightStarted) {
|
|
380
|
+
this.inFlightDeadlines.delete(state.requestId);
|
|
381
|
+
this.inFlightIps.delete(state.requestId);
|
|
382
|
+
}
|
|
355
383
|
} catch {
|
|
356
384
|
}
|
|
357
385
|
try {
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
import type { CartethyiaDatabase } from "../../persistence/db-handle";
|
|
11
11
|
import type { CanonicalRequest } from "../../protocol/canonical-model";
|
|
12
12
|
import type { PreparedProxyRequest } from "./preparer";
|
|
13
|
-
import { normalizeThinkingConfig } from "
|
|
13
|
+
import { normalizeThinkingConfig } from "../../protocol/translation/thinking";
|
|
14
14
|
import { compressRequest } from "./rtk/compress-request";
|
|
15
15
|
import { preferencesReaderFor } from "../../persistence/tenant-preferences";
|
|
16
16
|
import { resolveUpstreamTimeoutMs } from "../../config/timeouts";
|
|
@@ -35,7 +35,19 @@ import { providerHasAdapterUserAgent, providerUsesBespokeWire } from "../../prov
|
|
|
35
35
|
|
|
36
36
|
const CLAUDE_MODEL_FAMILIES = new Set(["opus", "sonnet", "haiku", "fable", "mythos"]);
|
|
37
37
|
|
|
38
|
+
/** Codex's built-in (non-routeable) model spellings worth matching. */
|
|
39
|
+
const CODEX_REVIEW_MODELS = new Set(["codex-auto-review"]);
|
|
40
|
+
|
|
38
41
|
export function cliMappingSourceKeys(toolId: string, sourceModel: string): readonly string[] {
|
|
42
|
+
if (toolId === "codex") {
|
|
43
|
+
const normalized = sourceModel.trim().toLowerCase();
|
|
44
|
+
if (CODEX_REVIEW_MODELS.has(normalized)) {
|
|
45
|
+
// Guardian's auto-review model is one fixed spelling; surface the full
|
|
46
|
+
// spelling plus the bare family so a slot mapped to either one hits.
|
|
47
|
+
return [...new Set([sourceModel, "codex-auto-review"])];
|
|
48
|
+
}
|
|
49
|
+
return [sourceModel];
|
|
50
|
+
}
|
|
39
51
|
if (toolId !== "claude") return [sourceModel];
|
|
40
52
|
const normalized = sourceModel.trim().toLowerCase();
|
|
41
53
|
const family =
|
|
@@ -199,23 +211,33 @@ export function capabilityProfileCacheSize(): number {
|
|
|
199
211
|
return capabilityProfileCache.size;
|
|
200
212
|
}
|
|
201
213
|
|
|
202
|
-
/** Resolves route identity
|
|
214
|
+
/** Resolves route identity: bundled adapters without a native User-Agent builder, plus custom providers. */
|
|
203
215
|
export function resolveRouteUserAgent(
|
|
204
216
|
providerId: string,
|
|
205
217
|
tenantUserAgent: string | undefined,
|
|
206
218
|
globalUserAgent: string | undefined,
|
|
207
219
|
): string | undefined {
|
|
208
|
-
if (
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
220
|
+
if (isBundledProviderId(providerId)) {
|
|
221
|
+
// A bundled adapter that frames its own identity headers never consumes a
|
|
222
|
+
// route User-Agent (Command Code, Qoder, buddy, ...), so it never gets one
|
|
223
|
+
// — the control is hidden for it too.
|
|
224
|
+
if (providerHasAdapterUserAgent(providerId)) return undefined;
|
|
225
|
+
// Bundled API-key providers: an operator who set no identity of their own
|
|
226
|
+
// gets the Claude Code CLI identity, resolved live so the stamped version
|
|
227
|
+
// tracks discovery. This used to be a frozen `codex_cli_rs` literal, which
|
|
228
|
+
// named a different vendor's client on requests that mostly reached neither.
|
|
229
|
+
const selected = resolveTenantOverride(
|
|
230
|
+
tenantUserAgent,
|
|
231
|
+
globalUserAgent,
|
|
232
|
+
buildClaudeCliUserAgent(),
|
|
233
|
+
);
|
|
234
|
+
return selected.trim() === "" ? undefined : selected;
|
|
235
|
+
}
|
|
236
|
+
// Custom (BYOK) providers: only an explicit operator choice rides on the
|
|
237
|
+
// route; the adapter owns the default otherwise (see `registerByokProviders`).
|
|
238
|
+
// An explicit empty choice ("None") is preserved so the adapter can honor it.
|
|
239
|
+
const selected = resolveTenantOverride(tenantUserAgent, globalUserAgent, undefined);
|
|
240
|
+
return selected === undefined ? undefined : selected.trim();
|
|
219
241
|
}
|
|
220
242
|
|
|
221
243
|
/** One persisted model row ready for candidate construction. */
|
|
@@ -741,43 +763,53 @@ class RouteCatalogRepository {
|
|
|
741
763
|
: {}),
|
|
742
764
|
};
|
|
743
765
|
if (account.status === "cooldown") {
|
|
744
|
-
//
|
|
745
|
-
//
|
|
746
|
-
//
|
|
766
|
+
// A row that still says `cooldown` stays excluded until the sweeper
|
|
767
|
+
// flips it back to `active`. An *expired* deadline used to read as
|
|
768
|
+
// healthy here (the comment below's "pure read" reasoning), which let
|
|
769
|
+
// routing redial the account in the window between the deadline
|
|
770
|
+
// elapsing and the next 30s sweep — reproducing the refusal that
|
|
771
|
+
// cooled it and logging a fresh `cooldown → cooldown` row each time
|
|
772
|
+
// (the operator's "still hit a cooled-down account" report). The
|
|
773
|
+
// sweep is the only exit from `cooldown`; until it runs the row is
|
|
774
|
+
// not proof the account can serve.
|
|
747
775
|
//
|
|
748
776
|
// `forceDisableCooldown` skips the exclusion entirely. The cooldown
|
|
749
777
|
// is still real and still recorded — it simply stops deciding
|
|
750
778
|
// routing. That is what makes the setting reversible: nothing was
|
|
751
779
|
// written, so turning it off needs no cleanup, and Health still
|
|
752
780
|
// reports the truth about the account.
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
+
const overrideActive =
|
|
782
|
+
rowTenantId === null
|
|
783
|
+
? anyTenantOverrides
|
|
784
|
+
: cooldownOverrideTenants.has(rowTenantId);
|
|
785
|
+
const accountDeadline = account.cooldownUntil;
|
|
786
|
+
const unexpired =
|
|
787
|
+
accountDeadline !== null && accountDeadline.getTime() > Date.now();
|
|
788
|
+
if (overrideActive) {
|
|
789
|
+
// The account is still cooling — this only stops it from being
|
|
790
|
+
// excluded. `plan()` reads `cooldown_until` to answer the
|
|
791
|
+
// all-cooling case, so it stays populated either way.
|
|
792
|
+
//
|
|
793
|
+
// Neither `health_status` nor `cooldown_kind` is set: the
|
|
794
|
+
// evaluator excludes a hard cooldown on `health_status ===
|
|
795
|
+
// "cooldown"` alone, so stamping it here would keep excluding
|
|
796
|
+
// the very accounts the override exists to reach — a spent quota
|
|
797
|
+
// or a policy block, which are the ones an operator actually
|
|
798
|
+
// wants to override.
|
|
799
|
+
if (unexpired) candidate.cooldown_until = accountDeadline!.getTime();
|
|
800
|
+
} else {
|
|
801
|
+
candidate.health_status = "cooldown";
|
|
802
|
+
candidate.cooldown_kind = hardCooldownCategory(account.lastErrorCategory)
|
|
803
|
+
? "hard"
|
|
804
|
+
: "soft";
|
|
805
|
+
// The deadline rides with the exclusion it explains. `plan()`
|
|
806
|
+
// reads it to answer the all-cooling case with the real reset
|
|
807
|
+
// instead of a bare 429, so a client backs off for the window
|
|
808
|
+
// that is actually open rather than inventing one. An expired
|
|
809
|
+
// deadline carries no reset — the sweep is about to recover the
|
|
810
|
+
// row, and quoting a past instant would tell the client to retry
|
|
811
|
+
// "immediately" for a wait that is already over.
|
|
812
|
+
if (unexpired) candidate.cooldown_until = accountDeadline!.getTime();
|
|
781
813
|
}
|
|
782
814
|
}
|
|
783
815
|
|
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
import {
|
|
22
22
|
candidateSupportsRequest,
|
|
23
23
|
type RequiredCapability,
|
|
24
|
-
} from "
|
|
24
|
+
} from "../../protocol/translation/capabilities";
|
|
25
25
|
import { planAllowsCandidate } from "./plan-gate";
|
|
26
26
|
import { InMemoryAdmissionController, admissionKey } from "./admission";
|
|
27
27
|
import { ReservationManager } from "./reservations";
|
|
@@ -32,7 +32,7 @@ import {
|
|
|
32
32
|
routeCandidateIndex,
|
|
33
33
|
} from "./candidate-index";
|
|
34
34
|
import { totalRemainingCredit, totalRemainingPercent, type ProviderQuotaWindow } from "../../providers/quota/quota-contracts";
|
|
35
|
-
import { getCachedQuotaEntry, quotaLens } from "../../
|
|
35
|
+
import { getCachedQuotaEntry, quotaLens } from "../../providers/quota/cache";
|
|
36
36
|
import type { RedisClient } from "../../persistence/redis";
|
|
37
37
|
import { sharedClaudeSlowLanes } from "../../providers/integrations/claude/claude-slow-lane";
|
|
38
38
|
|
|
@@ -579,6 +579,34 @@ export class RoutingEngine {
|
|
|
579
579
|
},
|
|
580
580
|
);
|
|
581
581
|
}
|
|
582
|
+
// The pinned account owns the live session, so it is the only correct
|
|
583
|
+
// target for this tool result — but health still applies. Forcing a
|
|
584
|
+
// session whose account is hard-cooling (quota exhausted) reproduces the
|
|
585
|
+
// refusal that cooled it, which is how "still hit a cooled-down account"
|
|
586
|
+
// leaked through continuation affinity. Evaluate the pin before
|
|
587
|
+
// returning: time-bounded cooling answers 429 with the real reset; an
|
|
588
|
+
// account that is disabled/locked answers a clear 400.
|
|
589
|
+
const pinnedDecisions = pinned.map((candidate) => this.eligibility.evaluate(candidate));
|
|
590
|
+
const blocked = pinnedDecisions.find((decision) => !decision.eligible);
|
|
591
|
+
if (blocked !== undefined) {
|
|
592
|
+
const reason = blocked.reason;
|
|
593
|
+
if (
|
|
594
|
+
reason === "cooldown_hard" ||
|
|
595
|
+
reason === "model_cooldown" ||
|
|
596
|
+
(reason === "quota_exhausted" && typeof blocked.cooldown_until === "number")
|
|
597
|
+
) {
|
|
598
|
+
throw accountsRateLimitedError(requestedModel, resolved.model, earliestResetMs(pinnedDecisions));
|
|
599
|
+
}
|
|
600
|
+
throw new GatewayError(
|
|
601
|
+
"invalid_request",
|
|
602
|
+
400,
|
|
603
|
+
"This tool result belongs to an account that is no longer routable. Reconnect the account, or resend the complete conversation.",
|
|
604
|
+
{
|
|
605
|
+
providerId: continuationAffinity.providerId,
|
|
606
|
+
accountId: continuationAffinity.providerAccountId,
|
|
607
|
+
},
|
|
608
|
+
);
|
|
609
|
+
}
|
|
582
610
|
return {
|
|
583
611
|
revision: snapshot.revision,
|
|
584
612
|
candidates: pinned,
|
|
@@ -6,7 +6,7 @@ import { parseToolDefinition } from "../dialects";
|
|
|
6
6
|
import { canContainToolResult } from "../../../protocol/canonical-model";
|
|
7
7
|
import { MessagesLedgerError } from "./errors";
|
|
8
8
|
import { finiteNumber, stringValue } from "../../../protocol/codecs";
|
|
9
|
-
import { LEVEL_TO_BUDGET } from "
|
|
9
|
+
import { LEVEL_TO_BUDGET } from "../../../protocol/translation/thinking";
|
|
10
10
|
|
|
11
11
|
export interface MessagesParseOptions {
|
|
12
12
|
/** Explicit Claude model generation (for example `4.6` or `4.7`). */
|
|
@@ -339,8 +339,7 @@ export class ResponsesEventEncoder extends SurfaceStreamEncoder<CanonicalEvent,
|
|
|
339
339
|
summary: [],
|
|
340
340
|
content: [],
|
|
341
341
|
};
|
|
342
|
-
if (content.encrypted_content
|
|
343
|
-
this.open = {
|
|
342
|
+
if (typeof content.encrypted_content === "string") item.encrypted_content = content.encrypted_content; this.open = {
|
|
344
343
|
kind: "reasoning",
|
|
345
344
|
item,
|
|
346
345
|
itemId,
|
|
@@ -501,7 +500,7 @@ export class ResponsesEventEncoder extends SurfaceStreamEncoder<CanonicalEvent,
|
|
|
501
500
|
);
|
|
502
501
|
summaryChunks.push(delta);
|
|
503
502
|
}
|
|
504
|
-
if (content.encrypted_content
|
|
503
|
+
if (typeof content.encrypted_content === "string")
|
|
505
504
|
events.push(
|
|
506
505
|
this.emit("response.reasoning.encrypted_content", {
|
|
507
506
|
item_id: this.open?.itemId,
|