cartethyia 2.6.7 → 2.7.1
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 +1 -7
- package/README.md +25 -2
- package/dist/dashboard/assets/{BannedPage-Dskhyp0l.js → BannedPage-D-J75hBM.js} +1 -1
- package/dist/dashboard/assets/BannedPage-D-J75hBM.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolDetailPage--SJhk7qQ.js → CliToolDetailPage-CVe6RaBH.js} +1 -1
- package/dist/dashboard/assets/CliToolDetailPage-CVe6RaBH.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolsPage-C203VT-c.js → CliToolsPage-Dd8ZSamp.js} +1 -1
- package/dist/dashboard/assets/CliToolsPage-Dd8ZSamp.js.gz +0 -0
- package/dist/dashboard/assets/{CombosPage-BcS7kGG1.js → CombosPage-QoJ6foxo.js} +1 -1
- package/dist/dashboard/assets/CombosPage-QoJ6foxo.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-DpNRay4C.js +1 -0
- package/dist/dashboard/assets/ConsoleLogPage-DpNRay4C.js.gz +0 -0
- package/dist/dashboard/assets/{CustomizationPage-uujbBYwC.js → CustomizationPage-CKUFTcW4.js} +1 -1
- package/dist/dashboard/assets/CustomizationPage-CKUFTcW4.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-CzSLiDzr.js +1 -0
- package/dist/dashboard/assets/HealthEventsModal-CzSLiDzr.js.gz +0 -0
- package/dist/dashboard/assets/LoginPage-DubdQcvX.js +1 -0
- package/dist/dashboard/assets/LoginPage-DubdQcvX.js.gz +0 -0
- package/dist/dashboard/assets/{ModelPicker-CA1kLs45.js → ModelPicker-BH8Tehdi.js} +1 -1
- package/dist/dashboard/assets/ModelPicker-BH8Tehdi.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-DuWzAQFL.js +1 -0
- package/dist/dashboard/assets/OverviewPage-DuWzAQFL.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-BKEKe2Fm.js +4 -0
- package/dist/dashboard/assets/ProviderDetailPage-BKEKe2Fm.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-Bwotu8UJ.js +1 -0
- package/dist/dashboard/assets/ProvidersPage-Bwotu8UJ.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-D7hpWbg9.js +9 -0
- package/dist/dashboard/assets/ProxyPage-D7hpWbg9.js.gz +0 -0
- package/dist/dashboard/assets/{QuotaPage-BueSWX5D.js → QuotaPage-ATpaxrbl.js} +1 -1
- package/dist/dashboard/assets/QuotaPage-ATpaxrbl.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-CETophpx.js +1 -0
- package/dist/dashboard/assets/SettingsPage-CETophpx.js.gz +0 -0
- package/dist/dashboard/assets/SetupPage-BdgECjqq.js +1 -0
- package/dist/dashboard/assets/SetupPage-BdgECjqq.js.gz +0 -0
- package/dist/dashboard/assets/{SortableList-DrKeMcGN.js → SortableList-CAK-EN9z.js} +1 -1
- package/dist/dashboard/assets/SortableList-CAK-EN9z.js.gz +0 -0
- package/dist/dashboard/assets/{StudioPage-qjuYtccc.js → StudioPage-B7W2_RhT.js} +3 -3
- package/dist/dashboard/assets/StudioPage-B7W2_RhT.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-6T4EFDjW.js +56 -0
- package/dist/dashboard/assets/UsagePage-6T4EFDjW.js.gz +0 -0
- package/dist/dashboard/assets/{api-keys-C23S9sgp.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-DU6qrMA_.js → assert-67xXH5rf.js} +1 -1
- package/dist/dashboard/assets/assert-67xXH5rf.js.gz +0 -0
- package/dist/dashboard/assets/{badge-Bkz_-RrM.js → badge-DC2L43Mj.js} +1 -1
- package/dist/dashboard/assets/badge-DC2L43Mj.js.gz +0 -0
- package/dist/dashboard/assets/{banner-C-7y3VKV.js → banner-BBVcb0J3.js} +1 -1
- package/dist/dashboard/assets/banner-BBVcb0J3.js.gz +0 -0
- package/dist/dashboard/assets/{card-BOPBuBzD.js → card-Cca1XC6G.js} +1 -1
- package/dist/dashboard/assets/card-Cca1XC6G.js.gz +0 -0
- package/dist/dashboard/assets/{cli-tools-BaFUuFFF.js → cli-tools-KvsWa1Jl.js} +1 -1
- package/dist/dashboard/assets/cli-tools-KvsWa1Jl.js.gz +0 -0
- package/dist/dashboard/assets/{clipboard-button-e-Mlk0FH.js → clipboard-button-DUzEbynq.js} +1 -1
- package/dist/dashboard/assets/clipboard-button-DUzEbynq.js.gz +0 -0
- package/dist/dashboard/assets/{dialog-Iec_ywbD.js → dialog-DFvWhwTa.js} +1 -1
- package/dist/dashboard/assets/dialog-DFvWhwTa.js.gz +0 -0
- package/dist/dashboard/assets/{drawer-Bs8MIIHc.js → drawer-CFM2sMX1.js} +1 -1
- package/dist/dashboard/assets/drawer-CFM2sMX1.js.gz +0 -0
- package/dist/dashboard/assets/{entry-DOddwoc0.js → entry-BLBLxX0h.js} +3 -3
- package/dist/dashboard/assets/entry-BLBLxX0h.js.gz +0 -0
- package/dist/dashboard/assets/{entry-DcobaKFu.js → entry-Bqy8RFvM.js} +1 -1
- package/dist/dashboard/assets/entry-Bqy8RFvM.js.gz +0 -0
- package/dist/dashboard/assets/{entry-BRszm4_Z.js → entry-_xHo5x6r.js} +1 -1
- package/dist/dashboard/assets/entry-_xHo5x6r.js.gz +0 -0
- package/dist/dashboard/assets/formatters-mpBPqAQ8.js +1 -0
- package/dist/dashboard/assets/formatters-mpBPqAQ8.js.gz +0 -0
- package/dist/dashboard/assets/{github-badge-BuDsrsPn.js → github-badge-C9e8Ts7e.js} +1 -1
- package/dist/dashboard/assets/github-badge-C9e8Ts7e.js.gz +0 -0
- package/dist/dashboard/assets/{index-BeLRhsmt.js → index-D6QzEC_n.js} +2 -2
- package/dist/dashboard/assets/index-D6QzEC_n.js.gz +0 -0
- package/dist/dashboard/assets/{inline-DToMICwl.js → inline-BFUcvllj.js} +1 -1
- package/dist/dashboard/assets/inline-BFUcvllj.js.gz +0 -0
- package/dist/dashboard/assets/{input-WlyHWeG2.js → input-C_ILAvBb.js} +1 -1
- package/dist/dashboard/assets/input-C_ILAvBb.js.gz +0 -0
- package/dist/dashboard/assets/{layout-DaJ5baJr.js → layout-CBIyT6Rt.js} +1 -1
- package/dist/dashboard/assets/layout-CBIyT6Rt.js.gz +0 -0
- package/dist/dashboard/assets/{list-row-B3oSK13z.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-ta6GMb-8.js → live-D0mm--ME.js} +1 -1
- package/dist/dashboard/assets/live-D0mm--ME.js.gz +0 -0
- package/dist/dashboard/assets/minimize-2-D-MN8W8a.js +1 -0
- package/dist/dashboard/assets/minimize-2-D-MN8W8a.js.gz +0 -0
- package/dist/dashboard/assets/{mutation-g_fduiNw.js → mutation-sMD965oO.js} +1 -1
- package/dist/dashboard/assets/mutation-sMD965oO.js.gz +0 -0
- package/dist/dashboard/assets/{numbers-Dulqgys5.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-UETqfytp.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-BPUo-W8d.js → routing-BnIpi1IJ.js} +1 -1
- package/dist/dashboard/assets/routing-BnIpi1IJ.js.gz +0 -0
- package/dist/dashboard/assets/{select-BfZ3SCTM.js → select-Cj5Nw43g.js} +1 -1
- package/dist/dashboard/assets/select-Cj5Nw43g.js.gz +0 -0
- package/dist/dashboard/assets/settings-BAFhDgX8.js +1 -0
- package/dist/dashboard/assets/settings-BAFhDgX8.js.gz +0 -0
- package/dist/dashboard/assets/{stack-B1w5BM0u.js → stack-EMrOMt4I.js} +1 -1
- package/dist/dashboard/assets/stack-EMrOMt4I.js.gz +0 -0
- package/dist/dashboard/assets/{state-DhZBfWgx.js → state-CWoo7ty5.js} +1 -1
- package/dist/dashboard/assets/state-CWoo7ty5.js.gz +0 -0
- package/dist/dashboard/assets/{switch-yD7D99Lg.js → switch-CCjT6IkF.js} +1 -1
- package/dist/dashboard/assets/switch-CCjT6IkF.js.gz +0 -0
- package/dist/dashboard/assets/{useMutation-EDFRkn1i.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 +55 -54
- 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 +1 -1
- package/scripts/cli/console-menu.ts +5 -2
- package/scripts/cli/tray.ts +21 -6
- package/src/boot/app.ts +24 -62
- package/src/boot/dependencies.ts +732 -750
- package/src/config/cooldowns.ts +16 -0
- package/src/config/providers.ts +13 -1
- package/src/config/resources.ts +13 -80
- package/src/config/security.ts +2 -38
- package/src/config/spec.ts +16 -23
- package/src/console/console-router.ts +7 -7
- package/src/console/domain-registration.ts +19 -12
- 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/remote-support/contracts.ts +3 -1
- package/src/console/remote-support/service.ts +115 -6
- 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 +16 -2
- package/src/network/outbound-fetch.ts +226 -365
- package/src/network/pool/agent.ts +8 -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 +15 -5
- package/src/observability/runtime-metrics.ts +30 -15
- 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 +0 -1
- 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 +21 -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/commandcode/commandcode.ts +134 -104
- package/src/providers/integrations/commandcode/discovery.ts +6 -5
- 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/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.ts +1641 -884
- 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 +1660 -0
- package/src/providers/operations/account-health-service.ts +621 -2097
- package/src/providers/operations/account-reset-service.ts +2 -0
- package/src/providers/provider-contracts.ts +0 -2
- 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/abuse.ts +17 -0
- 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 +224 -217
- 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 +59 -178
- package/src/transport/guards/abuse-guard.ts +10 -0
- package/src/transport/guards/auth-guard.ts +0 -18
- package/src/transport/guards/error-lifecycle.ts +1 -1
- package/src/transport/guards/pipeline.ts +0 -6
- package/src/transport/guards/request-context.ts +18 -91
- package/src/transport/request/degradation.ts +20 -1
- package/src/transport/request/inflight.ts +4 -95
- 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 +41 -31
- package/src/transport/routing/router.ts +2 -2
- package/src/transport/surface/messages/parse.ts +1 -1
- 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-Dskhyp0l.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage--SJhk7qQ.js.gz +0 -0
- package/dist/dashboard/assets/CliToolsPage-C203VT-c.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-BcS7kGG1.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-ChmiSLwO.js +0 -1
- package/dist/dashboard/assets/ConsoleLogPage-ChmiSLwO.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-uujbBYwC.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-fKAWd-OQ.js +0 -1
- package/dist/dashboard/assets/HealthEventsModal-fKAWd-OQ.js.gz +0 -0
- package/dist/dashboard/assets/LoginPage-DoOUthbG.js +0 -1
- package/dist/dashboard/assets/LoginPage-DoOUthbG.js.gz +0 -0
- package/dist/dashboard/assets/ModelPicker-CA1kLs45.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-C5Z-UBz1.js +0 -1
- package/dist/dashboard/assets/OverviewPage-C5Z-UBz1.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-CFu1qGcT.js +0 -3
- package/dist/dashboard/assets/ProviderDetailPage-CFu1qGcT.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-CnlJZbxF.js +0 -1
- package/dist/dashboard/assets/ProvidersPage-CnlJZbxF.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-B9mBTweh.js +0 -7
- package/dist/dashboard/assets/ProxyPage-B9mBTweh.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-BueSWX5D.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-B0x00MVY.js +0 -1
- package/dist/dashboard/assets/SettingsPage-B0x00MVY.js.gz +0 -0
- package/dist/dashboard/assets/SetupPage-DQ9oDRpi.js +0 -1
- package/dist/dashboard/assets/SetupPage-DQ9oDRpi.js.gz +0 -0
- package/dist/dashboard/assets/SortableList-DrKeMcGN.js.gz +0 -0
- package/dist/dashboard/assets/StudioPage-qjuYtccc.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-Be7aNviQ.js +0 -56
- package/dist/dashboard/assets/UsagePage-Be7aNviQ.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-C23S9sgp.js.gz +0 -0
- package/dist/dashboard/assets/app-BUSiYL6F.css +0 -2
- package/dist/dashboard/assets/app-BUSiYL6F.css.gz +0 -0
- package/dist/dashboard/assets/assert-DU6qrMA_.js.gz +0 -0
- package/dist/dashboard/assets/badge-Bkz_-RrM.js.gz +0 -0
- package/dist/dashboard/assets/banner-C-7y3VKV.js.gz +0 -0
- package/dist/dashboard/assets/card-BOPBuBzD.js.gz +0 -0
- package/dist/dashboard/assets/cli-tools-BaFUuFFF.js.gz +0 -0
- package/dist/dashboard/assets/clipboard-button-e-Mlk0FH.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-Iec_ywbD.js.gz +0 -0
- package/dist/dashboard/assets/drawer-Bs8MIIHc.js.gz +0 -0
- package/dist/dashboard/assets/entry-BRszm4_Z.js.gz +0 -0
- package/dist/dashboard/assets/entry-DOddwoc0.js.gz +0 -0
- package/dist/dashboard/assets/entry-DcobaKFu.js.gz +0 -0
- package/dist/dashboard/assets/github-badge-BuDsrsPn.js.gz +0 -0
- package/dist/dashboard/assets/index-BeLRhsmt.js.gz +0 -0
- package/dist/dashboard/assets/inline-DToMICwl.js.gz +0 -0
- package/dist/dashboard/assets/input-WlyHWeG2.js.gz +0 -0
- package/dist/dashboard/assets/layout-DaJ5baJr.js.gz +0 -0
- package/dist/dashboard/assets/list-row-B3oSK13z.js.gz +0 -0
- package/dist/dashboard/assets/live-ta6GMb-8.js.gz +0 -0
- package/dist/dashboard/assets/model-bans-BmYTmbAk.js +0 -1
- package/dist/dashboard/assets/model-bans-BmYTmbAk.js.gz +0 -0
- package/dist/dashboard/assets/mutation-g_fduiNw.js.gz +0 -0
- package/dist/dashboard/assets/numbers-Dulqgys5.js.gz +0 -0
- package/dist/dashboard/assets/query-keys-UETqfytp.js.gz +0 -0
- package/dist/dashboard/assets/routing-BPUo-W8d.js.gz +0 -0
- package/dist/dashboard/assets/select-BfZ3SCTM.js.gz +0 -0
- package/dist/dashboard/assets/settings--3hxSyry.js +0 -1
- package/dist/dashboard/assets/settings--3hxSyry.js.gz +0 -0
- package/dist/dashboard/assets/stack-B1w5BM0u.js.gz +0 -0
- package/dist/dashboard/assets/state-DhZBfWgx.js.gz +0 -0
- package/dist/dashboard/assets/switch-yD7D99Lg.js.gz +0 -0
- package/dist/dashboard/assets/useMutation-EDFRkn1i.js.gz +0 -0
- package/src/console/admin/model-abuse/contracts.ts +0 -38
- package/src/console/admin/model-abuse/routes.ts +0 -97
- package/src/security/model-abuse.ts +0 -704
- package/src/transport/guards/concurrency-guard.ts +0 -125
- /package/dist/dashboard/assets/{app-MdVfwraB.js → app-BaWd0puM.js} +0 -0
- /package/dist/dashboard/assets/{app-MdVfwraB.js.gz → app-BaWd0puM.js.gz} +0 -0
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import type { CanonicalMessage, CanonicalRequest, ContentPart } from "../canonical-model";
|
|
11
11
|
import { canContainToolResult, toolResultParts } from "../canonical-model";
|
|
12
|
-
import { isWebSearchTool } from "../../
|
|
13
|
-
import { reasoningEffortFromIntent } from "../../
|
|
12
|
+
import { isWebSearchTool } from "../../protocol/translation/search";
|
|
13
|
+
import { reasoningEffortFromIntent } from "../../protocol/reasoning";
|
|
14
14
|
import {
|
|
15
15
|
decodeCodexToolCallId,
|
|
16
16
|
encodeCodexToolCallId,
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
splitDataUrl,
|
|
15
15
|
type RewriteWalk,
|
|
16
16
|
} from "../codecs";
|
|
17
|
-
import { geminiThinkingOutputFloor } from "../../
|
|
17
|
+
import { geminiThinkingOutputFloor } from "../../protocol/reasoning";
|
|
18
18
|
import { base64DecodedByteLength, clampImage } from "../image-clamp";
|
|
19
19
|
|
|
20
20
|
export function geminiModelUrl(base: string, model: string, action: string): string {
|
|
@@ -2,15 +2,15 @@
|
|
|
2
2
|
* Canonical → Claude Messages request builder.
|
|
3
3
|
*/
|
|
4
4
|
import { allParts, type CanonicalRequest, type ContentPart } from "../canonical-model";
|
|
5
|
-
import { buildAnthropicThinkingPayload } from "../../
|
|
6
|
-
import { anthropicCacheControl } from "../../
|
|
5
|
+
import { buildAnthropicThinkingPayload } from "../../protocol/reasoning";
|
|
6
|
+
import { anthropicCacheControl } from "../../protocol/translation/cache-controls";
|
|
7
7
|
import {
|
|
8
8
|
THINKING_STRIPPED_SAMPLING_CONTROLS,
|
|
9
9
|
ensureMaxTokensForThinking,
|
|
10
10
|
isThinkingEnabled,
|
|
11
|
-
} from "../../
|
|
12
|
-
import { normalizeWireMaxTokens } from "../../
|
|
13
|
-
import { resolveWireMaxTokens, WIRE_TOKEN_BOUNDS } from "../../
|
|
11
|
+
} from "../../protocol/translation/capabilities";
|
|
12
|
+
import { normalizeWireMaxTokens } from "../../protocol/translation/normalize";
|
|
13
|
+
import { resolveWireMaxTokens, WIRE_TOKEN_BOUNDS } from "../../protocol/translation/quirks";
|
|
14
14
|
import {
|
|
15
15
|
isClaudeBillingHeaderText,
|
|
16
16
|
normalizeAnthropicToolCallId,
|
|
@@ -28,7 +28,7 @@ import {
|
|
|
28
28
|
} from "../codecs";
|
|
29
29
|
import { log } from "../../observability/logger";
|
|
30
30
|
import { base64DecodedByteLength, clampImage } from "../image-clamp";
|
|
31
|
-
import { clampReasoningEffort, claudeUsesAdaptiveThinking, LEVEL_TO_BUDGET, resolveSupportedReasoningEfforts } from "../../
|
|
31
|
+
import { clampReasoningEffort, claudeUsesAdaptiveThinking, LEVEL_TO_BUDGET, resolveSupportedReasoningEfforts } from "../../protocol/translation/thinking";
|
|
32
32
|
import { claudeResponseToEvents, parseClaudeSseStream } from "../response/messages";
|
|
33
33
|
|
|
34
34
|
/**
|
|
@@ -3,18 +3,18 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import type { CanonicalRequest } from "../canonical-model";
|
|
5
5
|
import { clampToolCallId, isRecord } from "../codecs";
|
|
6
|
-
import { markLatestBreakpoint } from "../../
|
|
7
|
-
import { pickWireSupportedControls } from "../../
|
|
8
|
-
import { normalizeGenerationControls } from "../../
|
|
9
|
-
import { resolveWireMaxTokens } from "../../
|
|
6
|
+
import { markLatestBreakpoint } from "../../protocol/translation/cache-controls";
|
|
7
|
+
import { pickWireSupportedControls } from "../../protocol/translation/capabilities";
|
|
8
|
+
import { normalizeGenerationControls } from "../../protocol/translation/normalize";
|
|
9
|
+
import { resolveWireMaxTokens } from "../../protocol/translation/quirks";
|
|
10
10
|
import {
|
|
11
11
|
clampReasoningEffort,
|
|
12
12
|
resolveSupportedReasoningEfforts,
|
|
13
|
-
} from "../../
|
|
13
|
+
} from "../../protocol/translation/thinking";
|
|
14
14
|
import { isClaudeBillingHeaderText, resolveImageSource, resolveOutputVerbosity } from "../codecs";
|
|
15
15
|
import { resolvePromptCacheKey } from "../../providers/operations/session-resolution";
|
|
16
|
-
import { reasoningEffortFromIntent } from "../../
|
|
17
|
-
import { isWebSearchTool } from "../../
|
|
16
|
+
import { reasoningEffortFromIntent } from "../../protocol/reasoning";
|
|
17
|
+
import { isWebSearchTool } from "../../protocol/translation/search";
|
|
18
18
|
import { parseResponsesResponseToEvents, decodeResponsesSseStream } from "../response/responses";
|
|
19
19
|
import { repairToolArguments } from "../../transport/surface/chat/encode";
|
|
20
20
|
|
|
@@ -1,98 +1,27 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* CodeBuddy-family DeepSeek sometimes emits native tool markup as assistant text.
|
|
3
3
|
* Complete declared calls are recovered; incomplete and undeclared blocks stay text.
|
|
4
|
+
*
|
|
5
|
+
* The mechanics live in `../dsml` with the CodeBuddy dialect as default; this
|
|
6
|
+
* module keeps the historical public surface (`DsmlToolCallParser`,
|
|
7
|
+
* `parseDsmlToolCalls`) stable for this wire family.
|
|
4
8
|
*/
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
+
import {
|
|
10
|
+
DsmlStreamParser,
|
|
11
|
+
type DsmlToolCall,
|
|
12
|
+
} from "../dsml";
|
|
9
13
|
|
|
10
|
-
|
|
11
|
-
function escapeRegExp(value: string): string {
|
|
12
|
-
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export interface DsmlToolCall {
|
|
16
|
-
readonly id: string;
|
|
17
|
-
readonly name: string;
|
|
18
|
-
readonly arguments: string;
|
|
19
|
-
}
|
|
14
|
+
export type { DsmlToolCall } from "../dsml";
|
|
20
15
|
|
|
21
16
|
export interface DsmlRecovery {
|
|
22
17
|
readonly text: string;
|
|
23
18
|
readonly calls: readonly DsmlToolCall[];
|
|
24
19
|
}
|
|
25
20
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
#pending = "";
|
|
29
|
-
#callSequence = 0;
|
|
30
|
-
|
|
21
|
+
/** Streaming strict-prefix parser with the CodeBuddy dialect defaults. */
|
|
22
|
+
export class DsmlToolCallParser extends DsmlStreamParser {
|
|
31
23
|
constructor(allowedNames: ReadonlySet<string>) {
|
|
32
|
-
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
push(chunk: string): DsmlRecovery {
|
|
36
|
-
this.#pending += chunk;
|
|
37
|
-
let cursor = 0;
|
|
38
|
-
let visible = "";
|
|
39
|
-
const calls: DsmlToolCall[] = [];
|
|
40
|
-
for (;;) {
|
|
41
|
-
let openIndex = -1;
|
|
42
|
-
let opener = "";
|
|
43
|
-
for (const candidate of OPENERS) {
|
|
44
|
-
const index = this.#pending.indexOf(candidate, cursor);
|
|
45
|
-
if (index >= 0 && (openIndex < 0 || index < openIndex)) {
|
|
46
|
-
openIndex = index;
|
|
47
|
-
opener = candidate;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
if (openIndex < 0) {
|
|
51
|
-
const safeEnd = this.#settledEnd(cursor);
|
|
52
|
-
visible += this.#pending.slice(cursor, safeEnd);
|
|
53
|
-
cursor = safeEnd;
|
|
54
|
-
break;
|
|
55
|
-
}
|
|
56
|
-
visible += this.#pending.slice(cursor, openIndex);
|
|
57
|
-
const separator = SEPARATORS.find((value) => opener.startsWith(`<${value}DSML`));
|
|
58
|
-
const blockName = BLOCK_NAMES.find((value) => opener.endsWith(` ${value}>`));
|
|
59
|
-
if (separator === undefined || blockName === undefined) {
|
|
60
|
-
visible += "<";
|
|
61
|
-
cursor = openIndex + 1;
|
|
62
|
-
continue;
|
|
63
|
-
}
|
|
64
|
-
const close = `</${separator}DSML${separator} ${blockName}>`;
|
|
65
|
-
const closeIndex = this.#pending.indexOf(close, openIndex + opener.length);
|
|
66
|
-
if (closeIndex < 0) {
|
|
67
|
-
cursor = openIndex;
|
|
68
|
-
break;
|
|
69
|
-
}
|
|
70
|
-
const end = closeIndex + close.length;
|
|
71
|
-
const block = this.#pending.slice(openIndex, end);
|
|
72
|
-
const parsed = parseBlock(block, opener, close, separator, this.#allowedNames);
|
|
73
|
-
if (parsed === undefined) visible += block;
|
|
74
|
-
else {
|
|
75
|
-
for (const call of parsed) calls.push({ ...call, id: `call_dsml_${this.#callSequence++}_${call.name}` });
|
|
76
|
-
}
|
|
77
|
-
cursor = end;
|
|
78
|
-
}
|
|
79
|
-
this.#pending = this.#pending.slice(cursor);
|
|
80
|
-
return { text: visible, calls };
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
flush(): DsmlRecovery {
|
|
84
|
-
const text = this.#pending;
|
|
85
|
-
this.#pending = "";
|
|
86
|
-
return { text, calls: [] };
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
#settledEnd(start: number): number {
|
|
90
|
-
const remaining = this.#pending.slice(start);
|
|
91
|
-
for (let length = Math.min(OPENERS.reduce((max, opener) => Math.max(max, opener.length - 1), 0), remaining.length); length > 0; length -= 1) {
|
|
92
|
-
const suffix = remaining.slice(-length);
|
|
93
|
-
if (OPENERS.some((opener) => opener.startsWith(suffix))) return this.#pending.length - length;
|
|
94
|
-
}
|
|
95
|
-
return this.#pending.length;
|
|
24
|
+
super({ allowedNames });
|
|
96
25
|
}
|
|
97
26
|
}
|
|
98
27
|
|
|
@@ -107,53 +36,3 @@ export function parseDsmlToolCalls(
|
|
|
107
36
|
const recovered = [...first.calls, ...last.calls];
|
|
108
37
|
return recovered.length === 0 ? undefined : { text: `${first.text}${last.text}`.trim(), calls: recovered };
|
|
109
38
|
}
|
|
110
|
-
|
|
111
|
-
function parseBlock(
|
|
112
|
-
block: string,
|
|
113
|
-
opener: string,
|
|
114
|
-
close: string,
|
|
115
|
-
separator: string,
|
|
116
|
-
allowedNames: ReadonlySet<string>,
|
|
117
|
-
): Omit<DsmlToolCall, "id">[] | undefined {
|
|
118
|
-
const inner = block.slice(opener.length, -close.length);
|
|
119
|
-
const pipe = escapeRegExp(separator);
|
|
120
|
-
const invokePattern = new RegExp(`<${pipe}DSML${pipe}\\s+invoke\\s+name="([^"]*)"\\s*>`, "g");
|
|
121
|
-
const calls: Omit<DsmlToolCall, "id">[] = [];
|
|
122
|
-
// The allowlist is strict only when the caller knows the declared tools: a
|
|
123
|
-
// caller without one (a direct parse) keeps the generic name rule, while a
|
|
124
|
-
// dispatch that declared its tools must never execute a name it did not offer.
|
|
125
|
-
const strict = allowedNames.size > 0;
|
|
126
|
-
let cursor = 0;
|
|
127
|
-
for (const invoke of inner.matchAll(invokePattern)) {
|
|
128
|
-
const name = invoke[1] ?? "";
|
|
129
|
-
if (!TOOL_NAME.test(name) || (strict && !allowedNames.has(name)) || invoke.index === undefined) return undefined;
|
|
130
|
-
if (inner.slice(cursor, invoke.index).trim().length > 0) return undefined;
|
|
131
|
-
const bodyStart = invoke.index + invoke[0].length;
|
|
132
|
-
const invokeClose = `</${separator}DSML${separator} invoke>`;
|
|
133
|
-
const invokeEnd = inner.indexOf(invokeClose, bodyStart);
|
|
134
|
-
if (invokeEnd < 0) return undefined;
|
|
135
|
-
const parameterPattern = new RegExp(`<${pipe}DSML${pipe}\\s+parameter\\s+name="([^"]*)"(?:\\s+string="(true|false)")?\\s*>`, "g");
|
|
136
|
-
const body = inner.slice(bodyStart, invokeEnd);
|
|
137
|
-
const args: Record<string, unknown> = {};
|
|
138
|
-
let parameterCursor = 0;
|
|
139
|
-
for (const parameter of body.matchAll(parameterPattern)) {
|
|
140
|
-
const key = parameter[1] ?? "";
|
|
141
|
-
if (key.length === 0 || parameter.index === undefined || body.slice(parameterCursor, parameter.index).trim().length > 0) return undefined;
|
|
142
|
-
const valueStart = parameter.index + parameter[0].length;
|
|
143
|
-
const parameterClose = `</${separator}DSML${separator} parameter>`;
|
|
144
|
-
const valueEnd = body.indexOf(parameterClose, valueStart);
|
|
145
|
-
if (valueEnd < 0) return undefined;
|
|
146
|
-
const raw = body.slice(valueStart, valueEnd);
|
|
147
|
-
if (parameter[2] === "true") args[key] = raw;
|
|
148
|
-
else {
|
|
149
|
-
try { args[key] = JSON.parse(raw); }
|
|
150
|
-
catch { args[key] = raw; }
|
|
151
|
-
}
|
|
152
|
-
parameterCursor = valueEnd + parameterClose.length;
|
|
153
|
-
}
|
|
154
|
-
if (body.slice(parameterCursor).trim().length > 0) return undefined;
|
|
155
|
-
calls.push({ name, arguments: JSON.stringify(args) });
|
|
156
|
-
cursor = invokeEnd + invokeClose.length;
|
|
157
|
-
}
|
|
158
|
-
return calls.length > 0 && inner.slice(cursor).trim().length === 0 ? calls : undefined;
|
|
159
|
-
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { resolveImageSource, splitDataUrl } from "
|
|
2
|
-
import type { CanonicalRequest, ContentPart } from "
|
|
1
|
+
import { resolveImageSource, splitDataUrl } from "../codecs";
|
|
2
|
+
import type { CanonicalRequest, ContentPart } from "../canonical-model";
|
|
3
3
|
import { log } from "../../observability/logger";
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
// Capability contracts, wire-family matrices, projection, and preflight.
|
|
2
|
-
import { capabilityUnsupported, GatewayError } from "
|
|
2
|
+
import { capabilityUnsupported, GatewayError } from "../gateway-error";
|
|
3
3
|
import {
|
|
4
4
|
summarizeParts,
|
|
5
5
|
type CacheHint,
|
|
6
6
|
type CanonicalRequest,
|
|
7
7
|
type GenerationControls,
|
|
8
8
|
type WireFamily,
|
|
9
|
-
} from "
|
|
9
|
+
} from "../canonical-model";
|
|
10
10
|
|
|
11
11
|
/** Capabilities declared by one resolved provider/model/route candidate. */
|
|
12
12
|
export interface RouteCapabilities {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Provider-neutral generation-controls normalization, used only after
|
|
2
2
|
// capability preflight.
|
|
3
|
-
import type { GenerationControls } from "
|
|
3
|
+
import type { GenerationControls } from "../canonical-model";
|
|
4
4
|
|
|
5
5
|
/** Normalizes the output-token control family onto `max_output_tokens`. */
|
|
6
6
|
export function normalizeGenerationControls(controls: GenerationControls): GenerationControls {
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
// Add a row ONLY when a concrete upstream 400/422 proves the quirk; do not
|
|
10
10
|
// speculate. The table is applied on the dispatch path before wire translation.
|
|
11
11
|
|
|
12
|
-
import type { CanonicalRequest, GenerationControls } from "
|
|
12
|
+
import type { CanonicalRequest, GenerationControls } from "../canonical-model";
|
|
13
13
|
import {
|
|
14
14
|
THINKING_STRIPPED_SAMPLING_CONTROLS,
|
|
15
15
|
isThinkingEnabled,
|
|
@@ -5,13 +5,14 @@
|
|
|
5
5
|
// so the native config (`thinking_type` + `budget_tokens`) is dropped while the
|
|
6
6
|
// request-level effort intent (OpenAI `reasoning_effort`) survives — that field
|
|
7
7
|
// is a property of the request, not of the turn.
|
|
8
|
-
import type { CanonicalRequest, ReasoningIntent, WireFamily } from "
|
|
8
|
+
import type { CanonicalRequest, ReasoningIntent, WireFamily } from "../canonical-model";
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Canonical reasoning effort ladder, ordered least to most intensive.
|
|
12
12
|
* Provides a unified effort scale across OpenAI, Anthropic, and open-weight models.
|
|
13
13
|
*/
|
|
14
14
|
export const REASONING_EFFORT_LADDER = [
|
|
15
|
+
"instant",
|
|
15
16
|
"minimal",
|
|
16
17
|
"low",
|
|
17
18
|
"medium",
|
|
@@ -54,6 +55,7 @@ export interface ParsedThinkingSuffix {
|
|
|
54
55
|
*/
|
|
55
56
|
export const LEVEL_TO_BUDGET: Readonly<Record<ReasoningEffortLevel | "none", number>> = {
|
|
56
57
|
none: 0,
|
|
58
|
+
instant: 256,
|
|
57
59
|
minimal: 512,
|
|
58
60
|
low: 1_024,
|
|
59
61
|
medium: 8_192,
|
|
@@ -241,6 +243,20 @@ export const BASE_SUPPORTED_EFFORTS: readonly ReasoningEffortLevel[] = [
|
|
|
241
243
|
*/
|
|
242
244
|
const MIMO_V26_SUPPORTED_EFFORTS: readonly ReasoningEffortLevel[] = ["low", "medium", "high"];
|
|
243
245
|
|
|
246
|
+
/**
|
|
247
|
+
* Mercury (Inception Labs) — the chat ladder is `{instant, low, medium,
|
|
248
|
+
* high}`: `minimal`, `xhigh`, and `max` each answer a 400
|
|
249
|
+
* `reasoning_effort must be one of: 'instant', 'low', 'medium', 'high'`.
|
|
250
|
+
* `instant` is not on the canonical ladder, so it can only appear here; the
|
|
251
|
+
* clamp steps down to `high` for any higher request.
|
|
252
|
+
*/
|
|
253
|
+
const INCEPTION_SUPPORTED_EFFORTS: readonly ReasoningEffortLevel[] = [
|
|
254
|
+
"instant",
|
|
255
|
+
"low",
|
|
256
|
+
"medium",
|
|
257
|
+
"high",
|
|
258
|
+
];
|
|
259
|
+
|
|
244
260
|
/**
|
|
245
261
|
* `low..max`: the OpenAI 5.6/6/daybreak generation and the new-gen Claude
|
|
246
262
|
* adaptive models (`fable`/`mythos`/`opus-5`/`sonnet-5`/`opus-4-7`/`opus-4-8`)
|
|
@@ -320,6 +336,12 @@ export function resolveSupportedReasoningEfforts(
|
|
|
320
336
|
if (id.includes("mimo-v2.6")) {
|
|
321
337
|
return MIMO_V26_SUPPORTED_EFFORTS;
|
|
322
338
|
}
|
|
339
|
+
// Checked before the generic default: Mercury's chat endpoint answers every
|
|
340
|
+
// effort outside {instant, low, medium, high} with a 400 that names the
|
|
341
|
+
// field (recorded live from request 40b60777).
|
|
342
|
+
if (id.includes("mercury") && wireFamily === "chat") {
|
|
343
|
+
return INCEPTION_SUPPORTED_EFFORTS;
|
|
344
|
+
}
|
|
323
345
|
// Claude catalog ladders: `output_config.effort` rides the
|
|
324
346
|
// Messages wire, so an out-of-ladder tier is an upstream rejection risk.
|
|
325
347
|
// Dot-tolerant (`cb/claude-opus-4.6`) because gateway-prefixed ids keep the
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
// strict wire encoder runs. The same original→sanitized mapping is applied to
|
|
7
7
|
// both the `toolCall` and its matching `toolResult` so pairing is preserved.
|
|
8
8
|
|
|
9
|
-
import type { CanonicalRequest } from "
|
|
9
|
+
import type { CanonicalRequest } from "../canonical-model";
|
|
10
10
|
|
|
11
11
|
/** Anthropic tool ID contract: alphanumerics, underscore, hyphen only. */
|
|
12
12
|
export const TOOL_ID_PATTERN: RegExp = /^[a-zA-Z0-9_-]+$/;
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
// winning route's capabilities and before projection — keeps the request
|
|
5
5
|
// dispatchable instead of failing upstream with a 400 the client cannot fix.
|
|
6
6
|
|
|
7
|
-
import type { CanonicalMessage, CanonicalRequest } from "
|
|
8
|
-
import { canContainToolResult } from "
|
|
7
|
+
import type { CanonicalMessage, CanonicalRequest } from "../canonical-model";
|
|
8
|
+
import { canContainToolResult } from "../canonical-model";
|
|
9
9
|
|
|
10
10
|
const MISSING_TOOL_OUTPUT = "<missing tool output>";
|
|
11
11
|
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
const MAX_COOLDOWN_MS = 24 * 3600 * 1000;
|
|
2
|
+
|
|
3
|
+
/** Parses Retry-After seconds or an HTTP date, bounded to one day. */
|
|
4
|
+
export function parseRetryAfter(value: string | null | undefined): number | null {
|
|
5
|
+
if (!value) return null;
|
|
6
|
+
const trimmed = value.trim();
|
|
7
|
+
const numeric = Number(trimmed);
|
|
8
|
+
if (Number.isFinite(numeric) && numeric > 0) return Math.min(MAX_COOLDOWN_MS, numeric * 1000);
|
|
9
|
+
const parsed = Date.parse(trimmed);
|
|
10
|
+
if (!Number.isFinite(parsed)) return null;
|
|
11
|
+
const delay = parsed - Date.now();
|
|
12
|
+
return delay > 0 ? Math.min(MAX_COOLDOWN_MS, delay) : null;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Parses x-ratelimit-reset as epoch seconds or a relative delay.
|
|
17
|
+
*
|
|
18
|
+
* Returns `null` — not `0` — when the instant has already passed. This matters
|
|
19
|
+
* because `parseUpstreamBackoff` chains these parsers with `??`, which falls
|
|
20
|
+
* through only on null: a `0` from an elapsed `x-ratelimit-reset` (a cached error
|
|
21
|
+
* body, clock skew, a stale edge response) stopped the chain before the
|
|
22
|
+
* lower-priority headers that carried the real wait. The `0` was then written to
|
|
23
|
+
* `details.retryAfterMs` and read by `classifyAccountError` as `headerCooldown`,
|
|
24
|
+
* which outranks the provider's own message text — so both were discarded and the
|
|
25
|
+
* account fell back to the generic default cooldown while the provider had parked
|
|
26
|
+
* it for the rest of the window.
|
|
27
|
+
*
|
|
28
|
+
* `parseRetryAfter` above already returns `null` for an elapsed date; matching it
|
|
29
|
+
* is what removes the asymmetry.
|
|
30
|
+
*/
|
|
31
|
+
export function parseRateLimitReset(value: string | null | undefined): number | null {
|
|
32
|
+
if (!value) return null;
|
|
33
|
+
const numeric = Number(value.trim());
|
|
34
|
+
if (!Number.isFinite(numeric) || numeric <= 0) return null;
|
|
35
|
+
const target = numeric >= 1_000_000_000 ? numeric * 1000 : Date.now() + numeric * 1000;
|
|
36
|
+
const remaining = target - Date.now();
|
|
37
|
+
return remaining > 0 ? Math.min(MAX_COOLDOWN_MS, remaining) : null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Parses a millisecond-denominated reset header (`retry-after-ms`, `x-ratelimit-reset-ms`). */
|
|
41
|
+
export function parseResetAfterMs(value: string | null | undefined): number | null {
|
|
42
|
+
if (!value) return null;
|
|
43
|
+
const numeric = Number(value.trim());
|
|
44
|
+
if (!Number.isFinite(numeric) || numeric <= 0) return null;
|
|
45
|
+
return Math.min(MAX_COOLDOWN_MS, Math.round(numeric));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Reads upstream backoff evidence in reference priority order: millisecond
|
|
50
|
+
* variants first (`retry-after-ms`, then `x-ratelimit-reset-ms`), then the
|
|
51
|
+
* classic `retry-after` / `x-ratelimit-reset` pair.
|
|
52
|
+
*/
|
|
53
|
+
export function parseUpstreamBackoff(headers: {
|
|
54
|
+
get(name: string): string | null;
|
|
55
|
+
}): number | null {
|
|
56
|
+
return (
|
|
57
|
+
parseResetAfterMs(headers.get("retry-after-ms")) ??
|
|
58
|
+
parseRetryAfter(headers.get("retry-after")) ??
|
|
59
|
+
parseResetAfterMs(headers.get("x-ratelimit-reset-ms")) ??
|
|
60
|
+
parseRateLimitReset(headers.get("x-ratelimit-reset")) ??
|
|
61
|
+
parseRateLimitReset(headers.get("x-ratelimit-reset-requests")) ??
|
|
62
|
+
parseRateLimitReset(headers.get("retry-after"))
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Extracts bounded reset durations from provider messages when headers omit
|
|
68
|
+
* them.
|
|
69
|
+
*
|
|
70
|
+
* Two shapes are recognized, because providers state resets both ways:
|
|
71
|
+
*
|
|
72
|
+
* - **Relative** — "quota will reset in 3 hours", "try again in 30s".
|
|
73
|
+
* - **Absolute** — "your usage will reset at 2026-09-24 02:12:51 UTC+8"
|
|
74
|
+
* (WorkBuddy/CodeBuddy `6004`). An absolute stamp is *not* a duration, and
|
|
75
|
+
* the relative-only pattern silently missed it, so the account fell back to
|
|
76
|
+
* the generic 15-minute default while the provider had actually parked it
|
|
77
|
+
* for the rest of the window — the account kept re-entering rotation and
|
|
78
|
+
* failing every request inside the stated reset window.
|
|
79
|
+
*/
|
|
80
|
+
/**
|
|
81
|
+
* One `amount unit` pair of a duration phrase: `4h`, `13m`, `2 hours`, `30s`.
|
|
82
|
+
*
|
|
83
|
+
* The unit ends on `(?![a-z])`, not `\b`: a compact compound like `2h30m` puts
|
|
84
|
+
* a digit straight after `h`, which is a word character, so a `\b` would refuse
|
|
85
|
+
* to end the pair and the whole phrase would parse as null. `m(?!s|o)` is what
|
|
86
|
+
* stops a bare `m` from swallowing `ms` (milliseconds) or `mo` (months),
|
|
87
|
+
* neither of which is a reset duration.
|
|
88
|
+
*/
|
|
89
|
+
const DURATION_PAIR_SOURCE = String.raw`(\d+(?:\.\d+)?)[\s-]*(hours?|hrs?|h|minutes?|mins?|m(?!s|o)|seconds?|secs?|s)(?![a-z])`;
|
|
90
|
+
|
|
91
|
+
function durationUnitMs(unit: string): number {
|
|
92
|
+
const first = unit.toLowerCase()[0];
|
|
93
|
+
return first === "h" ? 3600_000 : first === "m" ? 60_000 : 1_000;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Sums the `amount unit` pairs at the head of a duration phrase.
|
|
98
|
+
*
|
|
99
|
+
* Providers state a reset as one compound duration at least as often as a
|
|
100
|
+
* single unit: a daily-limit 429 reads "Try again in 4h 13m". A single-pair
|
|
101
|
+
* parser read only `4h` and dropped the 13 minutes, so the stored retry
|
|
102
|
+
* deadline was earlier than the window the provider had actually stated and the
|
|
103
|
+
* account re-entered rotation before it. Pairs are accepted only while they
|
|
104
|
+
* continue the phrase — separated by whitespace, a comma, or `and` — so a
|
|
105
|
+
* number that merely follows later in the sentence cannot be absorbed.
|
|
106
|
+
*/
|
|
107
|
+
function parseCompoundDuration(text: string): number | null {
|
|
108
|
+
const pairs = new RegExp(DURATION_PAIR_SOURCE, "gi");
|
|
109
|
+
let totalMs = 0;
|
|
110
|
+
let lastEnd = 0;
|
|
111
|
+
let matched = false;
|
|
112
|
+
for (let pair = pairs.exec(text); pair !== null; pair = pairs.exec(text)) {
|
|
113
|
+
const gap = text.slice(lastEnd, pair.index);
|
|
114
|
+
const continues = matched ? /^[\s,]*(?:and\s+)?$/i.test(gap) : /^\s*$/.test(gap);
|
|
115
|
+
if (!continues) break;
|
|
116
|
+
const amount = Number(pair[1]);
|
|
117
|
+
if (!Number.isFinite(amount) || amount <= 0) break;
|
|
118
|
+
totalMs += amount * durationUnitMs(pair[2] ?? "");
|
|
119
|
+
matched = true;
|
|
120
|
+
lastEnd = pair.index + pair[0].length;
|
|
121
|
+
}
|
|
122
|
+
return matched ? Math.round(totalMs) : null;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export function parseProviderResetDuration(message: string): number | null {
|
|
126
|
+
const trigger =
|
|
127
|
+
/(?:quota will reset|resets?|try again|retry(?:ing)?)\s+(?:in|after|over)\s+(?:a\s+)?(?:rolling\s+)?/i.exec(
|
|
128
|
+
message,
|
|
129
|
+
);
|
|
130
|
+
if (trigger) {
|
|
131
|
+
const parsed = parseCompoundDuration(message.slice(trigger.index + trigger[0].length));
|
|
132
|
+
if (parsed !== null) return Math.min(MAX_COOLDOWN_MS, parsed);
|
|
133
|
+
}
|
|
134
|
+
return parseAbsoluteResetTimestamp(message);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Reads an absolute reset instant out of a provider message, e.g.
|
|
139
|
+
* `will reset at 2026-09-24 02:12:51 UTC+8`. Returns the delay from now,
|
|
140
|
+
* bounded to one day, or null when no future timestamp is present.
|
|
141
|
+
*
|
|
142
|
+
* `Date.parse` cannot read the space-separated `YYYY-MM-DD HH:MM:SS` form with
|
|
143
|
+
* a bare `UTC±H` offset, so the parts are normalized explicitly rather than
|
|
144
|
+
* relying on engine-specific leniency.
|
|
145
|
+
*/
|
|
146
|
+
export function parseAbsoluteResetTimestamp(message: string): number | null {
|
|
147
|
+
const match = /(\d{4})-(\d{2})-(\d{2})[ T](\d{2}):(\d{2}):(\d{2})\s*(?:UTC|GMT)?\s*([+-]\d{1,2})?(?::?(\d{2}))?/i.exec(
|
|
148
|
+
message,
|
|
149
|
+
);
|
|
150
|
+
if (!match) return null;
|
|
151
|
+
const [, year, month, day, hour, minute, second, offsetHours, offsetMinutes] = match;
|
|
152
|
+
if (!year || !month || !day || !hour || !minute || !second) return null;
|
|
153
|
+
// A bare `UTC+8` means a whole-hour offset; `+08:30`-style halves are rare
|
|
154
|
+
// but cheap to honor. No offset at all is read as UTC (providers that omit
|
|
155
|
+
// it always label the stamp "UTC" in practice).
|
|
156
|
+
const sign = offsetHours?.startsWith("-") ? -1 : 1;
|
|
157
|
+
const offsetTotalMinutes =
|
|
158
|
+
offsetHours === undefined
|
|
159
|
+
? 0
|
|
160
|
+
: sign * (Math.abs(Number(offsetHours)) * 60 + Number(offsetMinutes ?? 0));
|
|
161
|
+
const utcMs = Date.UTC(
|
|
162
|
+
Number(year),
|
|
163
|
+
Number(month) - 1,
|
|
164
|
+
Number(day),
|
|
165
|
+
Number(hour),
|
|
166
|
+
Number(minute),
|
|
167
|
+
Number(second),
|
|
168
|
+
);
|
|
169
|
+
if (!Number.isFinite(utcMs)) return null;
|
|
170
|
+
const targetMs = utcMs - offsetTotalMinutes * 60_000;
|
|
171
|
+
const delay = targetMs - Date.now();
|
|
172
|
+
// A past stamp is not a usable reset (clock skew, stale cached error); fall
|
|
173
|
+
// through to the caller's default instead of returning a negative delay.
|
|
174
|
+
if (!Number.isFinite(delay) || delay <= 0) return null;
|
|
175
|
+
return Math.min(MAX_COOLDOWN_MS, Math.round(delay));
|
|
176
|
+
}
|
|
@@ -3,6 +3,7 @@ import type { CanonicalEvent, CanonicalRequest, WireFamily } from "../../protoco
|
|
|
3
3
|
import { isRecord } from "../../protocol/codecs";
|
|
4
4
|
import {
|
|
5
5
|
classifyUpstreamError,
|
|
6
|
+
isContextLengthFailure,
|
|
6
7
|
throwIfHtmlResponse,
|
|
7
8
|
mapUpstreamHttpError,
|
|
8
9
|
extractUpstreamMessage,
|
|
@@ -408,12 +409,20 @@ export class OpenAICompatibleAdapter extends BaseProviderAdapter {
|
|
|
408
409
|
value >= 400 &&
|
|
409
410
|
value < 600,
|
|
410
411
|
);
|
|
411
|
-
|
|
412
|
+
// A context-window overflow arrives here on the streaming path with a
|
|
413
|
+
// numeric code that says nothing (`11115`) and the real cause in
|
|
414
|
+
// `extError.message`. The status table alone classifies it as a generic
|
|
415
|
+
// refusal, so the one thing a client acts on — the measured prompt size
|
|
416
|
+
// against the window — never reached it. The prose/identifier check
|
|
417
|
+
// mirrors the non-streaming path's pre-table branch.
|
|
418
|
+
const classification = isContextLengthFailure(json)
|
|
419
|
+
? { code: "context_length_exceeded" as const, status: 413, origin: "upstream" as const, credentialEvidence: false, rateLimitScope: undefined }
|
|
420
|
+
: classifyUpstreamError(providerStatus, identifier);
|
|
412
421
|
const message = extractUpstreamMessage(json) || "Upstream reported an error";
|
|
413
422
|
throw new GatewayError(
|
|
414
423
|
classification.code,
|
|
415
424
|
classification.status,
|
|
416
|
-
message
|
|
425
|
+
message,
|
|
417
426
|
{
|
|
418
427
|
providerId: this.provider_id,
|
|
419
428
|
...(providerStatus === undefined ? {} : { providerStatus }),
|
|
@@ -294,17 +294,21 @@ async function disableAccount(
|
|
|
294
294
|
* "static token" toggle sets — the sweep skips it and its status stops reading
|
|
295
295
|
* as a broken/re-auth account. Not lease-fenced: a non-destructive annotation
|
|
296
296
|
* that any process may write, and the credential is not touched.
|
|
297
|
+
*
|
|
298
|
+
* This never changes `status`: a `disabled` account stays `disabled` (only an
|
|
299
|
+
* operator or an explicit recovery may re-enable it), while an `active` one
|
|
300
|
+
* stays `active`. The static marker and the error clearing are orthogonal to
|
|
301
|
+
* the account's health state.
|
|
297
302
|
*/
|
|
298
303
|
async function markStaticToken(db: CartethyiaDatabase, accountId: string): Promise<void> {
|
|
299
304
|
await db
|
|
300
305
|
.update(providerAccounts)
|
|
301
306
|
.set({
|
|
302
307
|
staticToken: true,
|
|
303
|
-
// A static token is a normal, usable credential, not an error state:
|
|
304
|
-
//
|
|
305
|
-
//
|
|
306
|
-
//
|
|
307
|
-
status: "active",
|
|
308
|
+
// A static token is a normal, usable credential, not an error state: any
|
|
309
|
+
// error left by the failed refresh would otherwise keep the row reading
|
|
310
|
+
// "Re-login required" while it is in fact serving. Mirrors the console's
|
|
311
|
+
// static-token toggle.
|
|
308
312
|
cooldownUntil: null,
|
|
309
313
|
lastError: null,
|
|
310
314
|
lastErrorCategory: null,
|