cartethyia 2.7.7 → 2.7.9
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 +0 -1
- package/README.md +29 -59
- package/dist/dashboard/assets/BannedPage-BxoG1MrL.js +1 -0
- package/dist/dashboard/assets/BannedPage-BxoG1MrL.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolDetailPage-CLUhmjUy.js → CliToolDetailPage-ixwgKk53.js} +1 -1
- package/dist/dashboard/assets/CliToolDetailPage-ixwgKk53.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolsPage-CQmrqqrk.js → CliToolsPage-BmttNV3U.js} +1 -1
- package/dist/dashboard/assets/CliToolsPage-BmttNV3U.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-BE3qwwfC.js +1 -0
- package/dist/dashboard/assets/CombosPage-BE3qwwfC.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-CRZdmMIX.js +1 -0
- package/dist/dashboard/assets/ConsoleLogPage-CRZdmMIX.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-Dv_FJ6Er.js +1 -0
- package/dist/dashboard/assets/CustomizationPage-Dv_FJ6Er.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-CoVrw2sM.js +1 -0
- package/dist/dashboard/assets/HealthEventsModal-CoVrw2sM.js.gz +0 -0
- package/dist/dashboard/assets/{InjectSystemPromptPanel-C-e2AMz6.js → InjectSystemPromptPanel-BOoO8rpz.js} +2 -2
- package/dist/dashboard/assets/InjectSystemPromptPanel-BOoO8rpz.js.gz +0 -0
- package/dist/dashboard/assets/{LoginPage-J8wPbmfT.js → LoginPage-ChJuNNVN.js} +1 -1
- package/dist/dashboard/assets/LoginPage-ChJuNNVN.js.gz +0 -0
- package/dist/dashboard/assets/{ModelPicker-C6WlpU5E.js → ModelPicker-BZAJtPg1.js} +1 -1
- package/dist/dashboard/assets/ModelPicker-BZAJtPg1.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-BE40s3u5.js +1 -0
- package/dist/dashboard/assets/OverviewPage-BE40s3u5.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-sfLoQtPV.js +8 -0
- package/dist/dashboard/assets/ProviderDetailPage-sfLoQtPV.js.gz +0 -0
- package/dist/dashboard/assets/ProviderIcon-CQ6bPUf_.js +1 -0
- package/dist/dashboard/assets/ProviderIcon-CQ6bPUf_.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-DcepmPWo.js +1 -0
- package/dist/dashboard/assets/ProvidersPage-DcepmPWo.js.gz +0 -0
- package/dist/dashboard/assets/{ProxyPage-BBTRBAQ3.js → ProxyPage-BJ87Pp0U.js} +1 -1
- package/dist/dashboard/assets/ProxyPage-BJ87Pp0U.js.gz +0 -0
- package/dist/dashboard/assets/{QuotaPage-B2WqMSG1.js → QuotaPage-gkSyRAKa.js} +1 -1
- package/dist/dashboard/assets/QuotaPage-gkSyRAKa.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-C5HPWqEv.js +1 -0
- package/dist/dashboard/assets/SettingsPage-C5HPWqEv.js.gz +0 -0
- package/dist/dashboard/assets/{SetupPage-LND6_Ss7.js → SetupPage-CFNcATJJ.js} +1 -1
- package/dist/dashboard/assets/SetupPage-CFNcATJJ.js.gz +0 -0
- package/dist/dashboard/assets/{StudioPage-BzNnm6Bi.js → StudioPage-YoD4tY5l.js} +1 -1
- package/dist/dashboard/assets/StudioPage-YoD4tY5l.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-DxjaAhb_.js +56 -0
- package/dist/dashboard/assets/UsagePage-DxjaAhb_.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-BTlkq_Uk.js +1 -0
- package/dist/dashboard/assets/api-keys-BTlkq_Uk.js.gz +0 -0
- package/dist/dashboard/assets/app-BX2nnKqs.css +2 -0
- package/dist/dashboard/assets/app-BX2nnKqs.css.gz +0 -0
- package/dist/dashboard/assets/assert-CndthXFQ.js +1 -0
- package/dist/dashboard/assets/assert-CndthXFQ.js.gz +0 -0
- package/dist/dashboard/assets/{badge-BaX2GTrn.js → badge-DdbT_7Am.js} +1 -1
- package/dist/dashboard/assets/badge-DdbT_7Am.js.gz +0 -0
- package/dist/dashboard/assets/ban-durations-B58FyRxC.js +1 -0
- package/dist/dashboard/assets/ban-durations-B58FyRxC.js.gz +0 -0
- package/dist/dashboard/assets/{banner-DFH4Bkte.js → banner-CsGN-nbm.js} +1 -1
- package/dist/dashboard/assets/banner-CsGN-nbm.js.gz +0 -0
- package/dist/dashboard/assets/brand/bansos_page.webp +0 -0
- package/dist/dashboard/assets/brand/denia_bansos.webp +0 -0
- package/dist/dashboard/assets/brand/favicon.webp +0 -0
- package/dist/dashboard/assets/brand/login_register.webp +0 -0
- package/dist/dashboard/assets/brand/login_register_initial.webp +0 -0
- package/dist/dashboard/assets/brand/og_bansos.webp +0 -0
- package/dist/dashboard/assets/brand/og_image.webp +0 -0
- package/dist/dashboard/assets/brand/sidebar_ico.webp +0 -0
- package/dist/dashboard/assets/{card-CxDw0lK6.js → card-mgZ_TLLZ.js} +1 -1
- package/dist/dashboard/assets/card-mgZ_TLLZ.js.gz +0 -0
- package/dist/dashboard/assets/cli-tools-A1-v8-03.js +1 -0
- package/dist/dashboard/assets/cli-tools-A1-v8-03.js.gz +0 -0
- package/dist/dashboard/assets/{clipboard-button-DfS1ntiq.js → clipboard-button-CJ4Zty89.js} +1 -1
- package/dist/dashboard/assets/clipboard-button-CJ4Zty89.js.gz +0 -0
- package/dist/dashboard/assets/{dialog-BK-NRr5u.js → dialog-B5L2jPQ_.js} +1 -1
- package/dist/dashboard/assets/dialog-B5L2jPQ_.js.gz +0 -0
- package/dist/dashboard/assets/{drawer-QqPul6sC.js → drawer-DkjUouAj.js} +1 -1
- package/dist/dashboard/assets/drawer-DkjUouAj.js.gz +0 -0
- package/dist/dashboard/assets/{entry-BMlYJVFj.js → entry-B-LVLV6o.js} +1 -1
- package/dist/dashboard/assets/entry-B-LVLV6o.js.gz +0 -0
- package/dist/dashboard/assets/entry-CAhdYl3B.js +1 -0
- package/dist/dashboard/assets/entry-CAhdYl3B.js.gz +0 -0
- package/dist/dashboard/assets/entry-D2xD0Tmd.js +4 -0
- package/dist/dashboard/assets/entry-D2xD0Tmd.js.gz +0 -0
- package/dist/dashboard/assets/{formatters-DP-5E01-.js → formatters-CrsdLVsn.js} +1 -1
- package/dist/dashboard/assets/formatters-CrsdLVsn.js.gz +0 -0
- package/dist/dashboard/assets/{index-D36vCZht.js → index-BU64Lqvp.js} +2 -2
- package/dist/dashboard/assets/index-BU64Lqvp.js.gz +0 -0
- package/dist/dashboard/assets/{inline-CHJQwxLa.js → inline-B2aDLyyg.js} +1 -1
- package/dist/dashboard/assets/inline-B2aDLyyg.js.gz +0 -0
- package/dist/dashboard/assets/{input-BlmlizVV.js → input-BCVkdR9d.js} +1 -1
- package/dist/dashboard/assets/input-BCVkdR9d.js.gz +0 -0
- package/dist/dashboard/assets/{layout-Bz93I-yE.js → layout-DNcAdMK-.js} +1 -1
- package/dist/dashboard/assets/layout-DNcAdMK-.js.gz +0 -0
- package/dist/dashboard/assets/{list-row-Ml16KCfu.js → list-row-DgrIH0hw.js} +1 -1
- package/dist/dashboard/assets/list-row-DgrIH0hw.js.gz +0 -0
- package/dist/dashboard/assets/{live-7_QpnYGu.js → live-Cae01iu8.js} +1 -1
- package/dist/dashboard/assets/live-Cae01iu8.js.gz +0 -0
- package/dist/dashboard/assets/{mutation-GU7XdEjz.js → mutation-BQ0ccH_A.js} +1 -1
- package/dist/dashboard/assets/mutation-BQ0ccH_A.js.gz +0 -0
- package/dist/dashboard/assets/{network-CuMDaTzT.js → network-CRib7H_2.js} +1 -1
- package/dist/dashboard/assets/network-CRib7H_2.js.gz +0 -0
- package/dist/dashboard/assets/{numbers-CoqpaDMx.js → numbers-BKQE8Xpz.js} +1 -1
- package/dist/dashboard/assets/numbers-BKQE8Xpz.js.gz +0 -0
- package/dist/dashboard/assets/{pagination-B5aOoweZ.js → pagination-ecPvG_8g.js} +1 -1
- package/dist/dashboard/assets/pagination-ecPvG_8g.js.gz +0 -0
- package/dist/dashboard/assets/providers/devin.svg +9 -0
- package/dist/dashboard/assets/providers/devin.svg.gz +0 -0
- package/dist/dashboard/assets/providers/windsurf.svg +10 -0
- package/dist/dashboard/assets/providers/windsurf.svg.gz +0 -0
- package/dist/dashboard/assets/{query-keys-YSoQOUh6.js → query-keys-IKLh0YFd.js} +1 -1
- package/dist/dashboard/assets/query-keys-IKLh0YFd.js.gz +0 -0
- package/dist/dashboard/assets/routing-Ckm1UXuC.js +1 -0
- package/dist/dashboard/assets/routing-Ckm1UXuC.js.gz +0 -0
- package/dist/dashboard/assets/{select-C9Ssle9b.js → select-ChoFWyKz.js} +1 -1
- package/dist/dashboard/assets/select-ChoFWyKz.js.gz +0 -0
- package/dist/dashboard/assets/settings-CKjFPlKW.js +1 -0
- package/dist/dashboard/assets/settings-CKjFPlKW.js.gz +0 -0
- package/dist/dashboard/assets/{stack-j7cXjgYX.js → stack-DWCwU3fh.js} +1 -1
- package/dist/dashboard/assets/stack-DWCwU3fh.js.gz +0 -0
- package/dist/dashboard/assets/{state-DHmEnLa2.js → state-Cg3VL5J_.js} +1 -1
- package/dist/dashboard/assets/state-Cg3VL5J_.js.gz +0 -0
- package/dist/dashboard/assets/story/25817331.webp +0 -0
- package/dist/dashboard/assets/story/Shorekeeper.webp +0 -0
- package/dist/dashboard/assets/story/cartethyia-god.webp +0 -0
- package/dist/dashboard/assets/story/fleurdelys_plus.webp +0 -0
- package/dist/dashboard/assets/story/jinhsi-blossom.webp +0 -0
- package/dist/dashboard/assets/story/phrolova-a.webp +0 -0
- package/dist/dashboard/assets/story/requestdeniawokkjpg.webp +0 -0
- package/dist/dashboard/assets/story/wuthering-waves-hiyuki-aemeath-hiyuki-rover.webp +0 -0
- package/dist/dashboard/assets/{switch-DmZcyTIC.js → switch-Db70BMix.js} +1 -1
- package/dist/dashboard/assets/switch-Db70BMix.js.gz +0 -0
- package/dist/dashboard/assets/{useMutation-CbCP4w1o.js → useMutation-BqdE0LuM.js} +1 -1
- package/dist/dashboard/assets/useMutation-BqdE0LuM.js.gz +0 -0
- package/dist/dashboard/console-backgrounds.webp +0 -0
- package/dist/dashboard/console-backgrounds_initial.webp +0 -0
- package/dist/dashboard/donation/qris-risun-marke.webp +0 -0
- package/dist/dashboard/index.html +3 -3
- package/dist/dashboard/index.html.gz +0 -0
- package/dist/dashboard/precompressed-assets.json +57 -55
- package/migrations/full-postgres/0044_add_models_warning_metadata.sql +10 -0
- package/migrations/full-postgres/0045_migrate_workbuddy_to_codebuddy.sql +156 -0
- package/migrations/lite-sqlite3/0044_add_models_warning_metadata.sql +11 -0
- package/migrations/lite-sqlite3/0045_migrate_workbuddy_to_codebuddy.sql +156 -0
- package/package.json +1 -1
- package/src/boot/dependencies.ts +11 -3
- package/src/config/cooldowns.ts +1 -1
- package/src/config/spec.ts +0 -6
- package/src/console/api-keys/routes.ts +11 -4
- package/src/console/console-router.ts +7 -0
- package/src/console/dashboard-assets.ts +5 -5
- package/src/console/domain-registration.ts +27 -0
- package/src/console/ip-bans/routes.ts +99 -0
- package/src/console/providers/catalog/catalog-projections.ts +5 -2
- package/src/console/providers/catalog/contracts.ts +123 -0
- package/src/console/providers/catalog/model-operations.ts +122 -10
- package/src/console/providers/catalog/provider-operations.ts +116 -8
- package/src/console/providers/catalog/public-model-store.ts +0 -0
- package/src/console/providers/catalog/routes.ts +64 -4
- package/src/console/providers/catalog/store-helpers.ts +30 -1
- package/src/console/providers/catalog/store.ts +243 -3
- package/src/console/quota/account-quota-global.ts +89 -1
- package/src/console/quota/native-identity.ts +2 -0
- package/src/console/settings/contracts.ts +18 -0
- package/src/console/settings/store.ts +32 -0
- package/src/console/system-prompt/contracts.ts +0 -1
- package/src/main.ts +1 -0
- package/src/observability/event-query.ts +18 -4
- package/src/persistence/dialect.ts +12 -0
- package/src/persistence/schema.ts +20 -2
- package/src/protocol/dsml.ts +1 -1
- package/src/protocol/gateway-error.ts +1 -0
- package/src/protocol/reasoning.ts +1 -1
- package/src/protocol/request/chat.ts +4 -4
- package/src/protocol/request/messages.ts +1 -1
- package/src/protocol/response/gemini.ts +6 -1
- package/src/protocol/sse-decoder.ts +25 -8
- package/src/protocol/stream-error-frames.ts +13 -3
- package/src/protocol/translation/quirks.ts +1 -1
- package/src/protocol/translation/tool-repair.ts +2 -2
- package/src/protocol/upstream/send-claude-messages.ts +15 -4
- package/src/protocol/upstream-parsing.ts +1 -1
- package/src/providers/adapters/compatible-adapter.ts +7 -5
- package/src/providers/authentication/oauth-refresh-service.ts +149 -36
- package/src/providers/default-registry.ts +68 -3
- package/src/providers/discovery/discovery-types.ts +12 -0
- package/src/providers/discovery/probe-phases.ts +61 -30
- package/src/providers/discovery/probing-service.ts +279 -164
- package/src/providers/integrations/antigravity/antigravity-protocol.ts +117 -2
- package/src/providers/integrations/antigravity/antigravity.ts +13 -2
- package/src/providers/integrations/buddy/buddy-catalog-shared.ts +72 -34
- package/src/providers/integrations/buddy/buddy-chat-shared.ts +18 -18
- package/src/providers/integrations/buddy/buddy-checkin.ts +17 -39
- package/src/providers/integrations/buddy/buddy-conversation-headers.ts +2 -2
- package/src/providers/integrations/buddy/buddy-discovery-shared.ts +5 -5
- package/src/providers/integrations/buddy/buddy-identity.ts +70 -0
- package/src/providers/integrations/buddy/buddy-media.ts +4 -5
- package/src/providers/integrations/buddy/buddy-oauth-shared.ts +2 -2
- package/src/providers/integrations/buddy/buddy-quota-shared.ts +1 -1
- package/src/providers/integrations/buddy/buddy-usage-shared.ts +1 -3
- package/src/providers/integrations/buddy/capabilities.ts +4 -41
- package/src/providers/integrations/buddy/codebuddy/cn-catalog.ts +2 -1
- package/src/providers/integrations/buddy/codebuddy/cn-provider.ts +20 -1
- package/src/providers/integrations/buddy/codebuddy/provider.ts +2 -2
- package/src/providers/integrations/buddy/codebuddy/shared.ts +6 -10
- package/src/providers/integrations/cursor/cursor-catalog.ts +2 -0
- package/src/providers/integrations/cursor/cursor-exec.ts +738 -0
- package/src/providers/integrations/cursor/cursor-http2.ts +2 -0
- package/src/providers/integrations/cursor/cursor-request.ts +9 -8
- package/src/providers/integrations/cursor/cursor-shared.ts +19 -1
- package/src/providers/integrations/cursor/cursor.ts +50 -30
- package/src/providers/integrations/cursor/protocol/tool-canonical.ts +26 -100
- package/src/providers/integrations/devin/devin-auth.ts +353 -0
- package/src/providers/integrations/devin/devin-catalog.ts +207 -0
- package/src/providers/integrations/devin/devin-http.ts +91 -0
- package/src/providers/integrations/devin/devin-quota.ts +214 -0
- package/src/providers/integrations/devin/devin-shared.ts +111 -0
- package/src/providers/integrations/devin/devin.ts +539 -0
- package/src/providers/integrations/devin/protocol/devin-proto.ts +584 -0
- package/src/providers/integrations/devin/protocol/framing.ts +124 -0
- package/src/providers/integrations/factory/factory.ts +2 -2
- package/src/providers/integrations/freepi/freepi-catalog.ts +60 -0
- package/src/providers/integrations/freepi/freepi-discovery.ts +82 -0
- package/src/providers/integrations/freepi/freepi-headers.ts +21 -0
- package/src/providers/integrations/freepi/freepi-oauth.ts +142 -0
- package/src/providers/integrations/freepi/freepi-protocol.ts +242 -0
- package/src/providers/integrations/freepi/freepi-quota.ts +126 -0
- package/src/providers/integrations/freepi/freepi-realm.ts +35 -0
- package/src/providers/integrations/freepi/freepi.ts +292 -0
- package/src/providers/integrations/gemini.ts +2 -2
- package/src/providers/integrations/hermes/hermes-oauth.ts +94 -0
- package/src/providers/integrations/hermes.ts +4 -12
- package/src/providers/integrations/opencode/fingerprint.ts +1 -1
- package/src/providers/integrations/perch/perch-catalog.ts +105 -0
- package/src/providers/integrations/perch/perch-oauth.ts +170 -0
- package/src/providers/integrations/perch/perch-protocol.ts +211 -0
- package/src/providers/integrations/perch/perch-quota.ts +137 -0
- package/src/providers/integrations/perch/perch-realm.ts +82 -0
- package/src/providers/integrations/perch/perch-system-prompt.ts +13 -0
- package/src/providers/integrations/perch/perch.ts +315 -0
- package/src/providers/integrations/perch/saffron-system-prompt.txt +8 -0
- package/src/providers/integrations/qoder/chat-proto.ts +328 -328
- package/src/providers/integrations/qoder/claim-qoder-credits.ts +1 -1
- package/src/providers/integrations/qoder/qoder-adapter.ts +89 -28
- package/src/providers/integrations/qoder/qoder-cache.ts +32 -0
- package/src/providers/integrations/qoder/qoder-catalog.ts +6 -1
- package/src/providers/integrations/qoder/qoder-checkin.ts +2 -2
- package/src/providers/integrations/qoder/qoder-grpc.ts +209 -5
- package/src/providers/integrations/qoder/qoder-ide.ts +5 -1
- package/src/providers/integrations/qoder/qoder-identity-cache.ts +184 -0
- package/src/providers/integrations/qoder/qoder-native-identity-store.ts +83 -18
- package/src/providers/integrations/qoder/qoder-realm.ts +9 -6
- package/src/providers/integrations/qoder/qoder-umid.ts +109 -342
- package/src/providers/integrations/qoder/qoder-virtual.ts +33 -1
- package/src/providers/integrations/qoder/qoder.ts +3 -6
- package/src/providers/integrations/trae/trae-catalog.ts +9 -14
- package/src/providers/integrations/trae/trae-headers.ts +36 -11
- package/src/providers/integrations/trae/trae-oauth.ts +14 -17
- package/src/providers/integrations/trae/trae-protocol.ts +3 -6
- package/src/providers/integrations/trae/trae-quota.ts +103 -21
- package/src/providers/integrations/trae/trae-realm.ts +36 -15
- package/src/providers/integrations/trae/trae.ts +97 -7
- package/src/providers/operations/account-error-classification.ts +66 -1
- package/src/providers/operations/account-health-service.ts +192 -26
- package/src/providers/operations/account-refresh-status.ts +284 -0
- package/src/providers/operations/account-tier.ts +67 -0
- package/src/providers/operations/byok-wire-profile.ts +9 -12
- package/src/providers/operations/client-versions.ts +37 -99
- package/src/providers/operations/cooldown-rules.ts +61 -0
- package/src/providers/operations/provider-catalog-seeder.ts +4 -0
- package/src/providers/provider-contracts.ts +29 -0
- package/src/providers/provider-metadata.ts +59 -4
- package/src/providers/provider-model-definition.ts +36 -0
- package/src/providers/quota/refresh.ts +21 -0
- package/src/security/abuse.ts +71 -0
- package/src/transport/dispatch/proxy-request.ts +1 -0
- package/src/transport/dispatch/streaming/stream.ts +1 -0
- package/src/transport/dispatch/upstream.ts +4 -1
- package/src/transport/failure-policy.ts +113 -7
- package/src/transport/routing/router.ts +25 -1
- package/src/transport/surface/messages/adapter.ts +2 -1
- package/src/transport/surface/responses/encode.ts +12 -1
- package/src/workers/daily-checkin.ts +1 -1
- package/src/workers/quota-refresh-sweep.ts +1 -1
- package/src/workers/scheduler.ts +22 -4
- package/dist/dashboard/assets/BannedPage-B05aF8Dg.js +0 -1
- package/dist/dashboard/assets/BannedPage-B05aF8Dg.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-CLUhmjUy.js.gz +0 -0
- package/dist/dashboard/assets/CliToolsPage-CQmrqqrk.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-CHG_1Wr3.js +0 -1
- package/dist/dashboard/assets/CombosPage-CHG_1Wr3.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-DPgbklad.js +0 -1
- package/dist/dashboard/assets/ConsoleLogPage-DPgbklad.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-pnfAqr_-.js +0 -1
- package/dist/dashboard/assets/CustomizationPage-pnfAqr_-.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-DGJuU2A6.js +0 -1
- package/dist/dashboard/assets/HealthEventsModal-DGJuU2A6.js.gz +0 -0
- package/dist/dashboard/assets/InjectSystemPromptPanel-C-e2AMz6.js.gz +0 -0
- package/dist/dashboard/assets/LoginPage-J8wPbmfT.js.gz +0 -0
- package/dist/dashboard/assets/ModelPicker-C6WlpU5E.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-BSQnF2Lp.js +0 -1
- package/dist/dashboard/assets/OverviewPage-BSQnF2Lp.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-DK_e0FJz.js +0 -7
- package/dist/dashboard/assets/ProviderDetailPage-DK_e0FJz.js.gz +0 -0
- package/dist/dashboard/assets/ProviderIcon-jjqXJDA3.js +0 -1
- package/dist/dashboard/assets/ProviderIcon-jjqXJDA3.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-D_a3r6Gc.js +0 -1
- package/dist/dashboard/assets/ProvidersPage-D_a3r6Gc.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-BBTRBAQ3.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-B2WqMSG1.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-D7xp5Ij_.js +0 -1
- package/dist/dashboard/assets/SettingsPage-D7xp5Ij_.js.gz +0 -0
- package/dist/dashboard/assets/SetupPage-LND6_Ss7.js.gz +0 -0
- package/dist/dashboard/assets/StudioPage-BzNnm6Bi.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-C1TbL24N.js +0 -56
- package/dist/dashboard/assets/UsagePage-C1TbL24N.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-KvX83mRe.js +0 -1
- package/dist/dashboard/assets/api-keys-KvX83mRe.js.gz +0 -0
- package/dist/dashboard/assets/app-mweRcsKp.css +0 -2
- package/dist/dashboard/assets/app-mweRcsKp.css.gz +0 -0
- package/dist/dashboard/assets/assert-CidQVoNo.js +0 -1
- package/dist/dashboard/assets/assert-CidQVoNo.js.gz +0 -0
- package/dist/dashboard/assets/badge-BaX2GTrn.js.gz +0 -0
- package/dist/dashboard/assets/banner-DFH4Bkte.js.gz +0 -0
- package/dist/dashboard/assets/brand/bansos_page.png +0 -0
- package/dist/dashboard/assets/brand/denia_bansos.jpg +0 -0
- package/dist/dashboard/assets/brand/favicon.jpg +0 -0
- package/dist/dashboard/assets/brand/login_register.jpg +0 -0
- package/dist/dashboard/assets/brand/login_register_initial.jpg +0 -0
- package/dist/dashboard/assets/brand/og_bansos.jpg +0 -0
- package/dist/dashboard/assets/brand/og_image.jpg +0 -0
- package/dist/dashboard/assets/brand/sidebar_ico.jpg +0 -0
- package/dist/dashboard/assets/card-CxDw0lK6.js.gz +0 -0
- package/dist/dashboard/assets/cli-tools-Bwv5-zGZ.js +0 -1
- package/dist/dashboard/assets/cli-tools-Bwv5-zGZ.js.gz +0 -0
- package/dist/dashboard/assets/clipboard-button-DfS1ntiq.js.gz +0 -0
- package/dist/dashboard/assets/dialog-BK-NRr5u.js.gz +0 -0
- package/dist/dashboard/assets/drawer-QqPul6sC.js.gz +0 -0
- package/dist/dashboard/assets/entry-BMlYJVFj.js.gz +0 -0
- package/dist/dashboard/assets/entry-Dmx7gBiN.js +0 -1
- package/dist/dashboard/assets/entry-Dmx7gBiN.js.gz +0 -0
- package/dist/dashboard/assets/entry-vqGb8yVz.js +0 -4
- package/dist/dashboard/assets/entry-vqGb8yVz.js.gz +0 -0
- package/dist/dashboard/assets/formatters-DP-5E01-.js.gz +0 -0
- package/dist/dashboard/assets/index-D36vCZht.js.gz +0 -0
- package/dist/dashboard/assets/inline-CHJQwxLa.js.gz +0 -0
- package/dist/dashboard/assets/input-BlmlizVV.js.gz +0 -0
- package/dist/dashboard/assets/layout-Bz93I-yE.js.gz +0 -0
- package/dist/dashboard/assets/list-row-Ml16KCfu.js.gz +0 -0
- package/dist/dashboard/assets/live-7_QpnYGu.js.gz +0 -0
- package/dist/dashboard/assets/minimize-2-BTmMTK4o.js +0 -1
- package/dist/dashboard/assets/minimize-2-BTmMTK4o.js.gz +0 -0
- package/dist/dashboard/assets/mutation-GU7XdEjz.js.gz +0 -0
- package/dist/dashboard/assets/network-CuMDaTzT.js.gz +0 -0
- package/dist/dashboard/assets/numbers-CoqpaDMx.js.gz +0 -0
- package/dist/dashboard/assets/pagination-B5aOoweZ.js.gz +0 -0
- package/dist/dashboard/assets/query-keys-YSoQOUh6.js.gz +0 -0
- package/dist/dashboard/assets/routing-fnCsxgNO.js +0 -1
- package/dist/dashboard/assets/routing-fnCsxgNO.js.gz +0 -0
- package/dist/dashboard/assets/select-C9Ssle9b.js.gz +0 -0
- package/dist/dashboard/assets/settings-BxslsErn.js +0 -1
- package/dist/dashboard/assets/settings-BxslsErn.js.gz +0 -0
- package/dist/dashboard/assets/stack-j7cXjgYX.js.gz +0 -0
- package/dist/dashboard/assets/state-DHmEnLa2.js.gz +0 -0
- package/dist/dashboard/assets/switch-DmZcyTIC.js.gz +0 -0
- package/dist/dashboard/assets/useMutation-CbCP4w1o.js.gz +0 -0
- package/dist/dashboard/console-backgrounds.jpg +0 -0
- package/dist/dashboard/console-backgrounds_initial.jpg +0 -0
- package/dist/dashboard/donation/qris-risun-marke.png +0 -0
- package/src/providers/integrations/buddy/turing-device-token.ts +0 -262
- package/src/providers/integrations/buddy/workbuddy/cn-oauth.ts +0 -30
- package/src/providers/integrations/buddy/workbuddy/cn-provider.ts +0 -58
- package/src/providers/integrations/buddy/workbuddy/oauth.ts +0 -31
- package/src/providers/integrations/buddy/workbuddy/provider.ts +0 -209
- package/src/providers/integrations/buddy/workbuddy/quota.ts +0 -161
- package/src/providers/integrations/buddy/workbuddy/shared.ts +0 -285
- /package/dist/dashboard/assets/{app-DRnQLhsc.js → app-B7kIWFm5.js} +0 -0
- /package/dist/dashboard/assets/{app-DRnQLhsc.js.gz → app-B7kIWFm5.js.gz} +0 -0
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
import { isRecord } from "../../protocol/codecs";
|
|
11
11
|
import { GatewayError } from "../../protocol/gateway-error";
|
|
12
12
|
import { BUDDY_PROVIDER_IDS } from "../provider-metadata";
|
|
13
|
+
import type { AccountTier } from "./account-tier";
|
|
13
14
|
import { parseProviderResetDuration, parseUpstreamBackoff } from "../../protocol/upstream-parsing";
|
|
14
15
|
import {
|
|
15
16
|
resolveAccountAuthRejectedCooldownMs,
|
|
@@ -23,6 +24,12 @@ import {
|
|
|
23
24
|
resolveAccountUnclassifiedCooldownMs,
|
|
24
25
|
} from "../../config/cooldowns";
|
|
25
26
|
const DEFAULT_RATE_LIMIT_COOLDOWN_MS = resolveAccountRateLimitCooldownMs;
|
|
27
|
+
/**
|
|
28
|
+
* Perch's `turn_rate_limited` window. The upstream does not state a reset and
|
|
29
|
+
* answers the same on every ticket mint while the window is exhausted, so a
|
|
30
|
+
* day matches the practical cadence and stops the redial loop.
|
|
31
|
+
*/
|
|
32
|
+
const PERCH_TURN_RATE_LIMIT_COOLDOWN_MS = 24 * 60 * 60 * 1000;
|
|
26
33
|
/**
|
|
27
34
|
* Inception's per-minute limit window (1 minute).
|
|
28
35
|
*
|
|
@@ -83,7 +90,7 @@ const QODER_QUOTA_COOLDOWN_MS = 24 * 60 * 60 * 1000;
|
|
|
83
90
|
* to be two constants, 24h for the content block and 6h for the channel one,
|
|
84
91
|
* both applied on the first observation. That was wrong in the direction that
|
|
85
92
|
* costs most: `11140` is also what a wrong desktop `User-Agent` brand answers
|
|
86
|
-
* (`
|
|
93
|
+
* (`codebuddy/shared.ts`), so after we fixed a header regression every buddy
|
|
87
94
|
* account stayed dead for up to a further day, and one prompt that failed a
|
|
88
95
|
* safety review cost a whole account a day. As a base it escalates through the
|
|
89
96
|
* consecutive-failure ladder, which tells the two causes apart by what they do
|
|
@@ -259,6 +266,12 @@ export interface AccountFailureEvidence {
|
|
|
259
266
|
* limit the provider said resets in 23 days.
|
|
260
267
|
*/
|
|
261
268
|
readonly upstreamBody?: unknown;
|
|
269
|
+
/**
|
|
270
|
+
* The account's tier, derived from its plan (`mapPlanToTier`). Read only by
|
|
271
|
+
* the writer when it resolves tier-aware cooldown rules; the classifier
|
|
272
|
+
* itself does not branch on it.
|
|
273
|
+
*/
|
|
274
|
+
readonly accountTier?: AccountTier;
|
|
262
275
|
}
|
|
263
276
|
|
|
264
277
|
export interface AccountErrorClassification {
|
|
@@ -299,6 +312,14 @@ export interface AccountErrorClassification {
|
|
|
299
312
|
* apart — it needs to know whether to count the hit and escalate the window.
|
|
300
313
|
*/
|
|
301
314
|
readonly modelUnsupported?: boolean;
|
|
315
|
+
/**
|
|
316
|
+
* Where the cooldown duration came from (D18). A duration resolved from a
|
|
317
|
+
* tier/provider rule (`provider-rule`) is a policy decision and must NOT go
|
|
318
|
+
* through `escalateFallbackCooldown` — the 1h escalation cap would cut the
|
|
319
|
+
* 24h Trae rule down to an hour on the first failure. Only a pure fallback
|
|
320
|
+
* (`fallback`) escalates.
|
|
321
|
+
*/
|
|
322
|
+
readonly ruleSource?: "provider-rule" | "fallback";
|
|
302
323
|
}
|
|
303
324
|
|
|
304
325
|
/**
|
|
@@ -344,6 +365,26 @@ export function escalateFallbackCooldown(
|
|
|
344
365
|
return Math.min(cooldownMs * 2 ** level, resolveAccountCooldownEscalationMaxMs());
|
|
345
366
|
}
|
|
346
367
|
|
|
368
|
+
/**
|
|
369
|
+
* Returns a cooldown duration with the D18 escalation split applied.
|
|
370
|
+
*
|
|
371
|
+
* A duration that came from a tier/provider rule (`ruleSource === "provider-rule"`)
|
|
372
|
+
* is a policy decision and must never pass through
|
|
373
|
+
* {@link escalateFallbackCooldown}: the 1h cap would truncate the 24h Trae
|
|
374
|
+
* free-tier rule to one hour on the first failure. Only a pure fallback
|
|
375
|
+
* (`ruleSource === "fallback"`, or unset for callers that predate the split)
|
|
376
|
+
* escalates.
|
|
377
|
+
*/
|
|
378
|
+
export function applyRuleEscalation(
|
|
379
|
+
cooldownMs: number,
|
|
380
|
+
providerStated: boolean,
|
|
381
|
+
consecutiveFailures: number,
|
|
382
|
+
ruleSource: "provider-rule" | "fallback" | undefined,
|
|
383
|
+
): number {
|
|
384
|
+
if (ruleSource === "provider-rule") return cooldownMs;
|
|
385
|
+
return escalateFallbackCooldown(cooldownMs, providerStated, consecutiveFailures);
|
|
386
|
+
}
|
|
387
|
+
|
|
347
388
|
/**
|
|
348
389
|
* The later of a candidate deadline and the one already recorded.
|
|
349
390
|
*
|
|
@@ -1594,6 +1635,30 @@ const ACCOUNT_ERROR_RULES: readonly AccountErrorRule[] = [
|
|
|
1594
1635
|
);
|
|
1595
1636
|
},
|
|
1596
1637
|
},
|
|
1638
|
+
{
|
|
1639
|
+
// Perch's `turn_rate_limited` is a per-turn API window, not a per-request
|
|
1640
|
+
// 429: the account keeps answering but every call mints another ticket
|
|
1641
|
+
// into the same exhausted window. The stated remedy ("contact us for
|
|
1642
|
+
// direct API access") implies the window is long — park the account for a
|
|
1643
|
+
// day so routing picks a sibling instead of hammering the same key.
|
|
1644
|
+
name: "perch-turn-rate-limit",
|
|
1645
|
+
match: (s) =>
|
|
1646
|
+
s.origin === "upstream" &&
|
|
1647
|
+
s.providerId === "perch" &&
|
|
1648
|
+
(s.providerCode === "turn_rate_limited" ||
|
|
1649
|
+
(s.statusCode === 429 && s.lower.includes("turn rate"))),
|
|
1650
|
+
decide: (s) => {
|
|
1651
|
+
const cooldownMs = s.statedCooldownMs ?? PERCH_TURN_RATE_LIMIT_COOLDOWN_MS;
|
|
1652
|
+
return s.result(
|
|
1653
|
+
"rate_limit_transient",
|
|
1654
|
+
"cooldown",
|
|
1655
|
+
cooldownMs,
|
|
1656
|
+
new Date(Date.now() + cooldownMs),
|
|
1657
|
+
s.reason("Perch turn rate limit"),
|
|
1658
|
+
s.origin === "upstream",
|
|
1659
|
+
);
|
|
1660
|
+
},
|
|
1661
|
+
},
|
|
1597
1662
|
{
|
|
1598
1663
|
name: "rate-limit",
|
|
1599
1664
|
match: (s) =>
|
|
@@ -1,18 +1,66 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { healthEventBuffer } from "../../persistence/health-event-flush";
|
|
3
|
-
import { and, count, desc, eq, inArray, isNotNull, lte, sql } from "drizzle-orm";
|
|
3
|
+
import { and, count, desc, eq, inArray, isNotNull, like, lte, or, sql } from "drizzle-orm";
|
|
4
4
|
import { affectedRows, pruneExpiredCooldowns } from "../../persistence/dialect";
|
|
5
5
|
import type { CartethyiaDatabase } from "../../persistence/db-handle";
|
|
6
|
-
import { healthEvents, providerAccounts } from "../../persistence/schema";
|
|
6
|
+
import { consoleSettings, healthEvents, providerAccounts } from "../../persistence/schema";
|
|
7
7
|
import {
|
|
8
8
|
COOLDOWN_RECOVERY_BATCH,
|
|
9
9
|
accountDeadlineMs,
|
|
10
|
+
applyRuleEscalation,
|
|
10
11
|
classifyAccountError,
|
|
11
|
-
escalateFallbackCooldown,
|
|
12
12
|
latestDeadlineMs,
|
|
13
13
|
unsupportedModelCooldownMs,
|
|
14
14
|
} from "./account-error-classification";
|
|
15
|
+
import { resolveAccountDisabledRecoveryMs } from "../../config/cooldowns";
|
|
15
16
|
import type { AccountErrorClassification, AccountFailureEvidence } from "./account-error-classification";
|
|
17
|
+
import { resolveCooldownRule } from "./cooldown-rules";
|
|
18
|
+
import { mapPlanToTier } from "./account-tier";
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Categories the auto-disable toggle (area 01) targets (D01). Model-scoped
|
|
22
|
+
* verdicts (`model_unreachable`) are not here — they belong to the tier rules
|
|
23
|
+
* (area 03, R-C).
|
|
24
|
+
*/
|
|
25
|
+
const AUTO_DISABLE_TARGET_CATEGORIES: ReadonlySet<string> = new Set([
|
|
26
|
+
"quota_exhausted",
|
|
27
|
+
"rate_limit_transient",
|
|
28
|
+
]);
|
|
29
|
+
|
|
30
|
+
/** Prefix marking an auto-disable written by the rate-limited intercept (D03/R-B). */
|
|
31
|
+
export const AUTO_DISABLE_MARKER_PREFIX = "auto-disable (rate-limited): ";
|
|
32
|
+
|
|
33
|
+
/** An upstream-stated window shorter than this is not auto-disabled (D04). */
|
|
34
|
+
const AUTO_DISABLE_STATED_MIN_MS = 5 * 60 * 1000;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Whether the auto-disable preference is on for a tenant (and for shared
|
|
38
|
+
* accounts — `tenantId === null` — when any tenant has it on, D06).
|
|
39
|
+
*
|
|
40
|
+
* The preference lives in `console_settings.preferences` (the C2 cluster added
|
|
41
|
+
* the typed field to `ConsoleSettingsPreferences`). Reading is fail-safe (D01
|
|
42
|
+
* §3.1): a missing row, a failed read, or a preference that is not exactly
|
|
43
|
+
* `true` all mean off — a settings failure must never break a health write.
|
|
44
|
+
*/
|
|
45
|
+
export async function autoDisableRateLimitedEnabled(
|
|
46
|
+
db: CartethyiaDatabase,
|
|
47
|
+
tenantId: string | null,
|
|
48
|
+
): Promise<boolean> {
|
|
49
|
+
try {
|
|
50
|
+
const rows = await db
|
|
51
|
+
.select({ tenantId: consoleSettings.tenantId, preferences: consoleSettings.preferences })
|
|
52
|
+
.from(consoleSettings);
|
|
53
|
+
return rows.some((row) => {
|
|
54
|
+
if (tenantId !== null && row.tenantId !== tenantId) return false;
|
|
55
|
+
const prefs = row.preferences as {
|
|
56
|
+
readonly autoDisableRateLimited?: boolean;
|
|
57
|
+
} | null | undefined;
|
|
58
|
+
return prefs?.autoDisableRateLimited === true;
|
|
59
|
+
});
|
|
60
|
+
} catch {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
16
64
|
|
|
17
65
|
export { classifyAccountError, unsupportedModelCooldownMs } from "./account-error-classification";
|
|
18
66
|
export type {
|
|
@@ -110,6 +158,11 @@ export async function recordAccountFailure(
|
|
|
110
158
|
// counted", which is the common case and must stay cheap.
|
|
111
159
|
modelBlockHits: providerAccounts.modelBlockHits,
|
|
112
160
|
cooldownUntil: providerAccounts.cooldownUntil,
|
|
161
|
+
// Required by the auto-disable toggle (tenant-level preference read)
|
|
162
|
+
// and by the tier-aware cooldown rules (plan → tier).
|
|
163
|
+
providerId: providerAccounts.providerId,
|
|
164
|
+
tenantId: providerAccounts.tenantId,
|
|
165
|
+
quotaPlan: providerAccounts.quotaPlan,
|
|
113
166
|
})
|
|
114
167
|
.from(providerAccounts)
|
|
115
168
|
.where(eq(providerAccounts.id, accountId))
|
|
@@ -128,6 +181,42 @@ export async function recordAccountFailure(
|
|
|
128
181
|
const fromStatus = account.status;
|
|
129
182
|
const now = new Date();
|
|
130
183
|
|
|
184
|
+
// ---- Auto-disable intercept (area 01, D01–D06) -------------------------
|
|
185
|
+
// Evaluated after classification and before the model-scoped/account-wide
|
|
186
|
+
// branch, so a 429/quota verdict carrying a modelId is rewritten to an
|
|
187
|
+
// account-wide disable instead of sinking into a per-model cooldown (D02).
|
|
188
|
+
//
|
|
189
|
+
// Only fires when every condition holds:
|
|
190
|
+
// - the tenant's toggle is on (fail-safe read: off on any error),
|
|
191
|
+
// - the classification mutates the account and the origin is upstream,
|
|
192
|
+
// - the category is a target (quota_exhausted / rate_limit_transient),
|
|
193
|
+
// - the verdict is a cooldown (rule-level `disabled` verdicts — bans,
|
|
194
|
+
// revoked credentials — are untouched),
|
|
195
|
+
// - the provider did not state a sub-5-minute window (D04: Inception's
|
|
196
|
+
// 1m limit must keep its short cooldown, not park the account for 24h).
|
|
197
|
+
const autoDisableTarget =
|
|
198
|
+
AUTO_DISABLE_TARGET_CATEGORIES.has(effective.category) &&
|
|
199
|
+
effective.status === "cooldown" &&
|
|
200
|
+
effective.mutatesAccount &&
|
|
201
|
+
options?.origin === "upstream";
|
|
202
|
+
const statedTooShort =
|
|
203
|
+
effective.providerStated === true &&
|
|
204
|
+
effective.cooldownMs > 0 &&
|
|
205
|
+
effective.cooldownMs < AUTO_DISABLE_STATED_MIN_MS;
|
|
206
|
+
let autoDisableActive = false;
|
|
207
|
+
if (autoDisableTarget && !statedTooShort) {
|
|
208
|
+
autoDisableActive = await autoDisableRateLimitedEnabled(client, account.tenantId);
|
|
209
|
+
}
|
|
210
|
+
// Rewritten classification used by the write paths below.
|
|
211
|
+
const interception = autoDisableActive
|
|
212
|
+
? {
|
|
213
|
+
...effective,
|
|
214
|
+
status: "disabled" as const,
|
|
215
|
+
cooldownMs: resolveAccountDisabledRecoveryMs(),
|
|
216
|
+
ruleSource: "fallback" as const,
|
|
217
|
+
}
|
|
218
|
+
: effective;
|
|
219
|
+
|
|
131
220
|
const modelId = options?.modelId;
|
|
132
221
|
// Every category here is a verdict about one (account, model) pair rather
|
|
133
222
|
// than about the credential, so all three back off that pair and leave the
|
|
@@ -138,16 +227,56 @@ export async function recordAccountFailure(
|
|
|
138
227
|
effective.category === "rate_limit_transient" ||
|
|
139
228
|
effective.category === "model_capacity" ||
|
|
140
229
|
effective.category === "model_unreachable";
|
|
141
|
-
|
|
230
|
+
// D02: when the auto-disable intercept fired, the target category must not
|
|
231
|
+
// sink into a model-scoped cooldown — the outcome is an account-wide
|
|
232
|
+
// disable.
|
|
233
|
+
const shouldModelCooldown = !autoDisableActive && modelId !== undefined && isThrottle;
|
|
142
234
|
// The account-wide streak, escalated for a provider-stated window. Read once
|
|
143
235
|
// so the model-scoped and account-wide paths below cannot disagree about it:
|
|
144
236
|
// this is the count *before* this failure, which the UPDATE below increments.
|
|
145
237
|
const streak = Math.max(0, account.consecutiveFailures ?? 0);
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
);
|
|
238
|
+
// Tier-aware rule resolution (area 03, D15–D19): the account's plan is
|
|
239
|
+
// projected on this row, mapped to a tier, and — when a rule exists for
|
|
240
|
+
// the (provider, category, tier) combination — replaces the classifier's
|
|
241
|
+
// duration. The rule duration is a policy decision and never escalates
|
|
242
|
+
// (D18); escalation applies only to the fallback.
|
|
243
|
+
const accountTier =
|
|
244
|
+
account.quotaPlan === null || account.quotaPlan === undefined
|
|
245
|
+
? null
|
|
246
|
+
: mapPlanToTier(account.providerId, account.quotaPlan);
|
|
247
|
+
// A provider-stated window always wins over a tier rule (D18), so the rule
|
|
248
|
+
// is only consulted when the upstream named nothing.
|
|
249
|
+
const ruleCooldownMs =
|
|
250
|
+
interception.providerStated === true
|
|
251
|
+
? undefined
|
|
252
|
+
: resolveCooldownRule({
|
|
253
|
+
providerId: account.providerId,
|
|
254
|
+
category: effective.category,
|
|
255
|
+
tier: accountTier,
|
|
256
|
+
});
|
|
257
|
+
const ruleSource = interception.ruleSource;
|
|
258
|
+
const sourceCooldownMs =
|
|
259
|
+
ruleCooldownMs !== undefined ? ruleCooldownMs : interception.cooldownMs;
|
|
260
|
+
const sourceRuleKind: "provider-rule" | "fallback" | undefined =
|
|
261
|
+
ruleCooldownMs !== undefined ? "provider-rule" : ruleSource;
|
|
262
|
+
// The auto-disable recovery window is a policy duration like a tier rule:
|
|
263
|
+
// it must never pass through the fallback escalation ladder, or the 1h cap
|
|
264
|
+
// would truncate the default 24h recovery on the first unstated 429 (D18).
|
|
265
|
+
const resolvedCooldownMs = autoDisableActive
|
|
266
|
+
? interception.cooldownMs
|
|
267
|
+
: applyRuleEscalation(
|
|
268
|
+
sourceCooldownMs,
|
|
269
|
+
interception.providerStated === true,
|
|
270
|
+
streak,
|
|
271
|
+
sourceRuleKind,
|
|
272
|
+
);
|
|
273
|
+
// When a rule replaces the duration, the classifier's `retryAt` (computed
|
|
274
|
+
// from the old constant/fallback) is stale: a shorter rule would lose to it
|
|
275
|
+
// in the monotonic-max deadline. Recompute from the rule duration instead.
|
|
276
|
+
const retryAtMs =
|
|
277
|
+
ruleCooldownMs !== undefined
|
|
278
|
+
? Date.now() + ruleCooldownMs
|
|
279
|
+
: effective.retryAt?.getTime();
|
|
151
280
|
|
|
152
281
|
// The unsupported-model ladder, computed before the branch: the classifier
|
|
153
282
|
// could only state the base window because the authoritative hit count
|
|
@@ -173,8 +302,8 @@ export async function recordAccountFailure(
|
|
|
173
302
|
// of the two whenever the provider really parked the pair longer.
|
|
174
303
|
const previousDeadline = existing[modelId] === undefined ? undefined : Date.parse(existing[modelId]);
|
|
175
304
|
const nextDeadline = latestDeadlineMs(
|
|
176
|
-
|
|
177
|
-
Date.now() + (unsupportedCooldownMs ??
|
|
305
|
+
retryAtMs,
|
|
306
|
+
Date.now() + (unsupportedCooldownMs ?? resolvedCooldownMs),
|
|
178
307
|
previousDeadline,
|
|
179
308
|
);
|
|
180
309
|
const nextUntil = new Date(nextDeadline).toISOString();
|
|
@@ -240,15 +369,35 @@ export async function recordAccountFailure(
|
|
|
240
369
|
// The route catalog projects modelCooldowns and stamps this candidate as
|
|
241
370
|
// model_cooldown. Invalidate when its deadline changes, or the cached
|
|
242
371
|
// snapshot keeps routing this pair until some unrelated write rebuilds it.
|
|
243
|
-
return {
|
|
372
|
+
return {
|
|
373
|
+
...classification,
|
|
374
|
+
routesVisible: previousDeadline !== nextDeadline,
|
|
375
|
+
...(sourceRuleKind === undefined ? {} : { ruleSource: sourceRuleKind }),
|
|
376
|
+
};
|
|
244
377
|
}
|
|
245
378
|
|
|
379
|
+
// When the intercept fired, the reason is prefixed with the auto-disable
|
|
380
|
+
// marker — `sweepExpiredCooldowns` (D03/R-B) uses the prefix to tell an
|
|
381
|
+
// auto-disable apart from an operator disable, which stays terminal.
|
|
382
|
+
const writtenReason = autoDisableActive
|
|
383
|
+
? `${AUTO_DISABLE_MARKER_PREFIX}${effective.reason}`
|
|
384
|
+
: effective.reason;
|
|
385
|
+
// A disabled account's deadline is the auto-disable recovery window — the
|
|
386
|
+
// classifier's `retryAt`/cooldown duration must not leak in, or a terminal
|
|
387
|
+
// disable (`recovery = 0`) would gain a deadline and be swept back.
|
|
388
|
+
const deadlineCandidates = autoDisableActive
|
|
389
|
+
? [interception.cooldownMs > 0 ? Date.now() + interception.cooldownMs : undefined]
|
|
390
|
+
: [
|
|
391
|
+
retryAtMs,
|
|
392
|
+
resolvedCooldownMs > 0 ? Date.now() + resolvedCooldownMs : undefined,
|
|
393
|
+
account.cooldownUntil?.getTime?.(),
|
|
394
|
+
];
|
|
246
395
|
await client
|
|
247
396
|
.update(providerAccounts)
|
|
248
397
|
.set({
|
|
249
|
-
status:
|
|
398
|
+
status: interception.status,
|
|
250
399
|
consecutiveFailures: sql`${providerAccounts.consecutiveFailures} + 1`,
|
|
251
|
-
lastError:
|
|
400
|
+
lastError: writtenReason,
|
|
252
401
|
lastErrorCategory: effective.category,
|
|
253
402
|
lastErrorAt: now,
|
|
254
403
|
// Monotonic against the account's own in-force deadline, for the same
|
|
@@ -259,11 +408,7 @@ export async function recordAccountFailure(
|
|
|
259
408
|
// (see `accountDeadlineMs`): the one state that asks for it is a
|
|
260
409
|
// terminal automatic disable, and an elapsed deadline there would hand
|
|
261
410
|
// the row straight back to the recovery sweep.
|
|
262
|
-
cooldownUntil: accountDeadlineMs(
|
|
263
|
-
effective.retryAt?.getTime(),
|
|
264
|
-
escalatedCooldownMs > 0 ? Date.now() + escalatedCooldownMs : undefined,
|
|
265
|
-
account.cooldownUntil?.getTime?.(),
|
|
266
|
-
]),
|
|
411
|
+
cooldownUntil: accountDeadlineMs(deadlineCandidates),
|
|
267
412
|
})
|
|
268
413
|
.where(eq(providerAccounts.id, accountId));
|
|
269
414
|
|
|
@@ -272,8 +417,8 @@ export async function recordAccountFailure(
|
|
|
272
417
|
entityKind: "account",
|
|
273
418
|
accountId,
|
|
274
419
|
fromStatus: fromStatus as "active" | "cooldown" | "disabled",
|
|
275
|
-
toStatus:
|
|
276
|
-
reason:
|
|
420
|
+
toStatus: interception.status,
|
|
421
|
+
reason: writtenReason,
|
|
277
422
|
errorCategory: effective.category,
|
|
278
423
|
modelId: modelId ?? null,
|
|
279
424
|
createdAt: now,
|
|
@@ -281,7 +426,11 @@ export async function recordAccountFailure(
|
|
|
281
426
|
}
|
|
282
427
|
// This path wrote `status` and `cooldownUntil`, both of which the snapshot
|
|
283
428
|
// projects, so routing has to see it.
|
|
284
|
-
return {
|
|
429
|
+
return {
|
|
430
|
+
...classification,
|
|
431
|
+
routesVisible: true,
|
|
432
|
+
...(sourceRuleKind === undefined ? {} : { ruleSource: sourceRuleKind }),
|
|
433
|
+
};
|
|
285
434
|
};
|
|
286
435
|
|
|
287
436
|
return withTransaction(db, persist, null);
|
|
@@ -414,11 +563,28 @@ export async function sweepExpiredCooldowns(db: CartethyiaDatabase): Promise<num
|
|
|
414
563
|
})
|
|
415
564
|
.from(providerAccounts)
|
|
416
565
|
.where(
|
|
417
|
-
// An ordinary cooldown, recovered as soon as its window elapses
|
|
566
|
+
// An ordinary cooldown, recovered as soon as its window elapses; plus
|
|
567
|
+
// (D03/R-B) an account the *auto-disable* intercept disabled — only
|
|
568
|
+
// when its category is a target AND the reason carries the auto-disable
|
|
569
|
+
// marker AND its recovery deadline has elapsed. An auto-disable is the
|
|
570
|
+
// machine's own verdict, so the sweep may undo it; an operator disable
|
|
571
|
+
// (no marker) stays terminal.
|
|
418
572
|
and(
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
573
|
+
or(
|
|
574
|
+
and(
|
|
575
|
+
eq(providerAccounts.status, "cooldown"),
|
|
576
|
+
isNotNull(providerAccounts.cooldownUntil),
|
|
577
|
+
lte(providerAccounts.cooldownUntil, now),
|
|
578
|
+
),
|
|
579
|
+
and(
|
|
580
|
+
eq(providerAccounts.status, "disabled"),
|
|
581
|
+
inArray(providerAccounts.lastErrorCategory, [...AUTO_DISABLE_TARGET_CATEGORIES]),
|
|
582
|
+
isNotNull(providerAccounts.lastError),
|
|
583
|
+
like(providerAccounts.lastError, `${AUTO_DISABLE_MARKER_PREFIX}%`),
|
|
584
|
+
isNotNull(providerAccounts.cooldownUntil),
|
|
585
|
+
lte(providerAccounts.cooldownUntil, now),
|
|
586
|
+
),
|
|
587
|
+
),
|
|
422
588
|
),
|
|
423
589
|
)
|
|
424
590
|
// One pass recovers a bounded batch. The sweep runs every 30s, so a
|