cartethyia 2.7.8 → 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-BRWKPQPf.js → CliToolDetailPage-ixwgKk53.js} +1 -1
- package/dist/dashboard/assets/CliToolDetailPage-ixwgKk53.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolsPage-BP-Y5iHN.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-Cv-2Ol_K.js → InjectSystemPromptPanel-BOoO8rpz.js} +2 -2
- package/dist/dashboard/assets/InjectSystemPromptPanel-BOoO8rpz.js.gz +0 -0
- package/dist/dashboard/assets/{LoginPage-bKfz_b1K.js → LoginPage-ChJuNNVN.js} +1 -1
- package/dist/dashboard/assets/LoginPage-ChJuNNVN.js.gz +0 -0
- package/dist/dashboard/assets/{ModelPicker-B-WQxx_H.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-emlTuxz6.js → ProxyPage-BJ87Pp0U.js} +1 -1
- package/dist/dashboard/assets/ProxyPage-BJ87Pp0U.js.gz +0 -0
- package/dist/dashboard/assets/{QuotaPage-CMJKNYWb.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-CUZrKs9r.js → SetupPage-CFNcATJJ.js} +1 -1
- package/dist/dashboard/assets/SetupPage-CFNcATJJ.js.gz +0 -0
- package/dist/dashboard/assets/{StudioPage-jWmUdDpy.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-DyI_mJfi.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-B-yXtCqb.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-_HwAEqFF.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-ByjCk0o9.js → clipboard-button-CJ4Zty89.js} +1 -1
- package/dist/dashboard/assets/clipboard-button-CJ4Zty89.js.gz +0 -0
- package/dist/dashboard/assets/{dialog-BZhOlfz-.js → dialog-B5L2jPQ_.js} +1 -1
- package/dist/dashboard/assets/dialog-B5L2jPQ_.js.gz +0 -0
- package/dist/dashboard/assets/{drawer-BADqIRYP.js → drawer-DkjUouAj.js} +1 -1
- package/dist/dashboard/assets/drawer-DkjUouAj.js.gz +0 -0
- package/dist/dashboard/assets/{entry-COaC-irN.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-DcHJyRiu.js → formatters-CrsdLVsn.js} +1 -1
- package/dist/dashboard/assets/formatters-CrsdLVsn.js.gz +0 -0
- package/dist/dashboard/assets/{index-CgKmFngv.js → index-BU64Lqvp.js} +2 -2
- package/dist/dashboard/assets/index-BU64Lqvp.js.gz +0 -0
- package/dist/dashboard/assets/{inline-B2BirKry.js → inline-B2aDLyyg.js} +1 -1
- package/dist/dashboard/assets/inline-B2aDLyyg.js.gz +0 -0
- package/dist/dashboard/assets/{input-D-qV9dKY.js → input-BCVkdR9d.js} +1 -1
- package/dist/dashboard/assets/input-BCVkdR9d.js.gz +0 -0
- package/dist/dashboard/assets/{layout-B7tVWhtV.js → layout-DNcAdMK-.js} +1 -1
- package/dist/dashboard/assets/layout-DNcAdMK-.js.gz +0 -0
- package/dist/dashboard/assets/{list-row-CtkGE3bF.js → list-row-DgrIH0hw.js} +1 -1
- package/dist/dashboard/assets/list-row-DgrIH0hw.js.gz +0 -0
- package/dist/dashboard/assets/{live-DGizF06-.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-C9UE-bOr.js → network-CRib7H_2.js} +1 -1
- package/dist/dashboard/assets/network-CRib7H_2.js.gz +0 -0
- package/dist/dashboard/assets/{numbers-BnAd22a_.js → numbers-BKQE8Xpz.js} +1 -1
- package/dist/dashboard/assets/numbers-BKQE8Xpz.js.gz +0 -0
- package/dist/dashboard/assets/{pagination-BAywyMzS.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-BCOcv8WK.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-ByKt3Q1g.js → stack-DWCwU3fh.js} +1 -1
- package/dist/dashboard/assets/stack-DWCwU3fh.js.gz +0 -0
- package/dist/dashboard/assets/{state-DXbGTx7N.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-ZeTsW623.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 +210 -6
- 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/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-DeIbIiBU.js +0 -1
- package/dist/dashboard/assets/BannedPage-DeIbIiBU.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-BRWKPQPf.js.gz +0 -0
- package/dist/dashboard/assets/CliToolsPage-BP-Y5iHN.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-B7JHozVa.js +0 -1
- package/dist/dashboard/assets/CombosPage-B7JHozVa.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-GX7iGGcu.js +0 -1
- package/dist/dashboard/assets/ConsoleLogPage-GX7iGGcu.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-HVonbGEa.js +0 -1
- package/dist/dashboard/assets/CustomizationPage-HVonbGEa.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-xaut4OJ9.js +0 -1
- package/dist/dashboard/assets/HealthEventsModal-xaut4OJ9.js.gz +0 -0
- package/dist/dashboard/assets/InjectSystemPromptPanel-Cv-2Ol_K.js.gz +0 -0
- package/dist/dashboard/assets/LoginPage-bKfz_b1K.js.gz +0 -0
- package/dist/dashboard/assets/ModelPicker-B-WQxx_H.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-Z9pJZFo8.js +0 -1
- package/dist/dashboard/assets/OverviewPage-Z9pJZFo8.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-CMeDBxR1.js +0 -7
- package/dist/dashboard/assets/ProviderDetailPage-CMeDBxR1.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-BXPdOpG4.js +0 -1
- package/dist/dashboard/assets/ProvidersPage-BXPdOpG4.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-emlTuxz6.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-CMJKNYWb.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-BSy7EwGo.js +0 -1
- package/dist/dashboard/assets/SettingsPage-BSy7EwGo.js.gz +0 -0
- package/dist/dashboard/assets/SetupPage-CUZrKs9r.js.gz +0 -0
- package/dist/dashboard/assets/StudioPage-jWmUdDpy.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-5F9tGVGG.js +0 -56
- package/dist/dashboard/assets/UsagePage-5F9tGVGG.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-BEsU8hKs.css +0 -2
- package/dist/dashboard/assets/app-BEsU8hKs.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-DyI_mJfi.js.gz +0 -0
- package/dist/dashboard/assets/banner-B-yXtCqb.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-_HwAEqFF.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-ByjCk0o9.js.gz +0 -0
- package/dist/dashboard/assets/dialog-BZhOlfz-.js.gz +0 -0
- package/dist/dashboard/assets/drawer-BADqIRYP.js.gz +0 -0
- package/dist/dashboard/assets/entry-COaC-irN.js.gz +0 -0
- package/dist/dashboard/assets/entry-Ca7LSCet.js +0 -4
- package/dist/dashboard/assets/entry-Ca7LSCet.js.gz +0 -0
- package/dist/dashboard/assets/entry-PDd27uCd.js +0 -1
- package/dist/dashboard/assets/entry-PDd27uCd.js.gz +0 -0
- package/dist/dashboard/assets/formatters-DcHJyRiu.js.gz +0 -0
- package/dist/dashboard/assets/index-CgKmFngv.js.gz +0 -0
- package/dist/dashboard/assets/inline-B2BirKry.js.gz +0 -0
- package/dist/dashboard/assets/input-D-qV9dKY.js.gz +0 -0
- package/dist/dashboard/assets/layout-B7tVWhtV.js.gz +0 -0
- package/dist/dashboard/assets/list-row-CtkGE3bF.js.gz +0 -0
- package/dist/dashboard/assets/live-DGizF06-.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-C9UE-bOr.js.gz +0 -0
- package/dist/dashboard/assets/numbers-BnAd22a_.js.gz +0 -0
- package/dist/dashboard/assets/pagination-BAywyMzS.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-BCOcv8WK.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-ByKt3Q1g.js.gz +0 -0
- package/dist/dashboard/assets/state-DXbGTx7N.js.gz +0 -0
- package/dist/dashboard/assets/switch-ZeTsW623.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-CTxoqyYB.js → app-B7kIWFm5.js} +0 -0
- /package/dist/dashboard/assets/{app-CTxoqyYB.js.gz → app-B7kIWFm5.js.gz} +0 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { t } from "elysia";
|
|
2
2
|
import type { Elysia } from "elysia";
|
|
3
|
-
import { and, eq, inArray, isNull } from "drizzle-orm";
|
|
3
|
+
import { and, eq, inArray, isNull, sql } from "drizzle-orm";
|
|
4
|
+
import { isSqliteDatabase } from "../../persistence/dialect";
|
|
4
5
|
import { adminAuditLog, providerAccounts } from "../../persistence/schema";
|
|
5
6
|
import { requireGlobalAdmin } from "../shared/errors";
|
|
6
7
|
import {
|
|
@@ -26,6 +27,24 @@ import {
|
|
|
26
27
|
type AccountQuotaRoutesDeps,
|
|
27
28
|
type AccountStatus,
|
|
28
29
|
} from "./account-quota-shared";
|
|
30
|
+
import type { ProviderAccountBulkActionResult } from "../providers/catalog/contracts";
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* The same live-cooldown predicate as the tenant-scoped store, evaluated in
|
|
34
|
+
* SQL so one statement covers the whole shared-account set. `status='cooldown'`
|
|
35
|
+
* counts even when its deadline elapsed; a live `model_cooldowns` entry counts
|
|
36
|
+
* even while `status` is still `active` (D07).
|
|
37
|
+
*/
|
|
38
|
+
function globalLiveCooldownPredicate(database: unknown): ReturnType<typeof and> {
|
|
39
|
+
const nowIso = new Date().toISOString();
|
|
40
|
+
const liveModelCooldown = isSqliteDatabase(database)
|
|
41
|
+
? sql`EXISTS (SELECT 1 FROM json_each(${providerAccounts.modelCooldowns}) AS entry WHERE json_valid(${providerAccounts.modelCooldowns}) AND json_type(${providerAccounts.modelCooldowns}) = 'object' AND entry.value > ${nowIso})`
|
|
42
|
+
: sql`EXISTS (SELECT 1 FROM jsonb_each_text(${providerAccounts.modelCooldowns}) AS entry WHERE jsonb_typeof(${providerAccounts.modelCooldowns}) = 'object' AND entry.value > ${nowIso})`;
|
|
43
|
+
return and(
|
|
44
|
+
isNull(providerAccounts.tenantId),
|
|
45
|
+
sql`${providerAccounts.status} = 'cooldown' OR ${providerAccounts.cooldownUntil} > ${nowIso} OR ${liveModelCooldown}`,
|
|
46
|
+
)!;
|
|
47
|
+
}
|
|
29
48
|
|
|
30
49
|
/**
|
|
31
50
|
* Registers the platform-admin account/quota routes onto `app`. Every route here
|
|
@@ -38,6 +57,47 @@ export function registerAccountQuotaGlobalRoutes(
|
|
|
38
57
|
refreshDeps: QuotaRefreshDeps,
|
|
39
58
|
): void {
|
|
40
59
|
const { db, accessResolver, redis, snapshotInvalidator } = deps;
|
|
60
|
+
/**
|
|
61
|
+
* The two bulk actions are one statement each, scoped strictly to shared
|
|
62
|
+
* rows. Neither action takes a provider filter: the global-admin surface
|
|
63
|
+
* lists shared accounts across providers, and the same health predicate
|
|
64
|
+
* defines the target set everywhere.
|
|
65
|
+
*/
|
|
66
|
+
const runGlobalBulk = async (
|
|
67
|
+
kind: "disable" | "delete",
|
|
68
|
+
): Promise<ProviderAccountBulkActionResult> => {
|
|
69
|
+
if (kind === "disable") {
|
|
70
|
+
const updated = await db
|
|
71
|
+
.update(providerAccounts)
|
|
72
|
+
.set({ status: "disabled" })
|
|
73
|
+
.where(globalLiveCooldownPredicate(db))
|
|
74
|
+
.returning({ id: providerAccounts.id });
|
|
75
|
+
invalidateCredentialCaches(updated.map((row) => row.id));
|
|
76
|
+
await snapshotInvalidator?.invalidate();
|
|
77
|
+
return { ok: true, updated: updated.length, globalSkipped: 0 };
|
|
78
|
+
}
|
|
79
|
+
const deleted = await db
|
|
80
|
+
.delete(providerAccounts)
|
|
81
|
+
.where(
|
|
82
|
+
and(
|
|
83
|
+
isNull(providerAccounts.tenantId),
|
|
84
|
+
eq(providerAccounts.status, "disabled"),
|
|
85
|
+
eq(providerAccounts.lastErrorCategory, "policy_blocked_banned"),
|
|
86
|
+
),
|
|
87
|
+
)
|
|
88
|
+
.returning({ id: providerAccounts.id });
|
|
89
|
+
const deletedIds = deleted.map((row) => row.id);
|
|
90
|
+
invalidateCredentialCaches(deletedIds);
|
|
91
|
+
await invalidateQuotaCaches(GLOBAL_QUOTA_LENS, deletedIds, redis);
|
|
92
|
+
await snapshotInvalidator?.invalidate();
|
|
93
|
+
return {
|
|
94
|
+
ok: true,
|
|
95
|
+
updated: 0,
|
|
96
|
+
deleted: deletedIds,
|
|
97
|
+
missing: [],
|
|
98
|
+
globalSkipped: 0,
|
|
99
|
+
};
|
|
100
|
+
};
|
|
41
101
|
app
|
|
42
102
|
.get("/global/accounts", async ({ request }) => {
|
|
43
103
|
requireGlobalAdmin(accessResolver(request));
|
|
@@ -157,6 +217,34 @@ export function registerAccountQuotaGlobalRoutes(
|
|
|
157
217
|
});
|
|
158
218
|
return { ok: true, deleted: deletedIds, missing };
|
|
159
219
|
})
|
|
220
|
+
/**
|
|
221
|
+
* Global counterparts of the provider-detail bulk actions (D10): shared
|
|
222
|
+
* accounts carry `tenant_id IS NULL`, so the tenant-scoped pair can never
|
|
223
|
+
* match them. Both are one statement, one audit row, one snapshot
|
|
224
|
+
* invalidation, and authorize through `requireGlobalAdmin`.
|
|
225
|
+
*/
|
|
226
|
+
.post("/global/accounts/actions/disable-cooldowns", async ({ request }) => {
|
|
227
|
+
const access = requireGlobalAdmin(accessResolver(request));
|
|
228
|
+
const { ok, updated, globalSkipped } = await runGlobalBulk("disable");
|
|
229
|
+
await db.insert(adminAuditLog).values({
|
|
230
|
+
actor: access.admissionIdentity,
|
|
231
|
+
action: "provider_account.bulk_disabled_cooldowns",
|
|
232
|
+
target: "global",
|
|
233
|
+
detail: { updated, globalSkipped },
|
|
234
|
+
});
|
|
235
|
+
return { ok, updated, globalSkipped };
|
|
236
|
+
})
|
|
237
|
+
.post("/global/accounts/actions/delete-banned", async ({ request }) => {
|
|
238
|
+
const access = requireGlobalAdmin(accessResolver(request));
|
|
239
|
+
const { ok, updated, deleted, missing, globalSkipped } = await runGlobalBulk("delete");
|
|
240
|
+
await db.insert(adminAuditLog).values({
|
|
241
|
+
actor: access.admissionIdentity,
|
|
242
|
+
action: "provider_account.bulk_deleted_banned",
|
|
243
|
+
target: "global",
|
|
244
|
+
detail: { deleted: deleted ?? [], missing: missing ?? [], globalSkipped },
|
|
245
|
+
});
|
|
246
|
+
return { ok, updated, deleted: deleted ?? [], missing: missing ?? [], globalSkipped };
|
|
247
|
+
})
|
|
160
248
|
.post("/global/accounts/:id/quota/refresh", async ({ request, params, set }) => {
|
|
161
249
|
const access = requireGlobalAdmin(accessResolver(request));
|
|
162
250
|
const target = await loadQuotaTarget(db, params.id, null);
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
DrizzleQoderNativeIdentityStore,
|
|
17
17
|
type QoderNativeIdentityStore,
|
|
18
18
|
} from "../../providers/integrations/qoder/qoder-native-identity-store";
|
|
19
|
+
import { qoderRealmForProvider } from "../../providers/integrations/qoder/qoder-realm";
|
|
19
20
|
import { ConsoleDomainError, requireGlobalAdmin, requireTenantScope } from "../shared/errors";
|
|
20
21
|
import type { ConsoleAccessResolver } from "../auth/access";
|
|
21
22
|
|
|
@@ -38,6 +39,7 @@ export type NativeIdentityAllocator = (args: {
|
|
|
38
39
|
function defaultAllocator(store: QoderNativeIdentityStore): NativeIdentityAllocator {
|
|
39
40
|
return ({ providerId, accountId, avoidToken }) =>
|
|
40
41
|
allocateQoderNativeIdentity(store, providerId, accountId, {
|
|
42
|
+
realm: qoderRealmForProvider(providerId),
|
|
41
43
|
...(avoidToken !== undefined ? { avoidToken } : {}),
|
|
42
44
|
});
|
|
43
45
|
}
|
|
@@ -59,6 +59,15 @@ export interface RuntimeSettingsResponse {
|
|
|
59
59
|
* used instead.
|
|
60
60
|
*/
|
|
61
61
|
readonly visionHandoffModels: readonly string[];
|
|
62
|
+
/**
|
|
63
|
+
* Auto-disable provider accounts stuck on upstream rate-limit/quota verdicts.
|
|
64
|
+
* Default false — the risky direction stays off until an operator flips it.
|
|
65
|
+
*/
|
|
66
|
+
readonly autoDisableRateLimited: boolean;
|
|
67
|
+
/** Modelprobe mode; `detail` when unset. */
|
|
68
|
+
readonly modelProbeMode: ModelProbeMode;
|
|
69
|
+
/** Fast-probe timeout in seconds; null means the built-in default (10). */
|
|
70
|
+
readonly modelProbeFastTimeoutSeconds: number | null;
|
|
62
71
|
readonly updatedAt: string;
|
|
63
72
|
/*
|
|
64
73
|
* What each `null` actually resolves to, in the same units the row stores.
|
|
@@ -136,6 +145,10 @@ export type PonyTailLevel = (typeof PONYTAIL_LEVELS)[number];
|
|
|
136
145
|
/** RTK prune strength accepted by the runtime-settings PATCH. */
|
|
137
146
|
export type RtkLevel = (typeof RTK_LEVELS)[number];
|
|
138
147
|
|
|
148
|
+
export const MODEL_PROBE_MODES = ["fast", "detail"] as const;
|
|
149
|
+
/** Modelprobe mode accepted by the runtime-settings PATCH and the probe route. */
|
|
150
|
+
export type ModelProbeMode = (typeof MODEL_PROBE_MODES)[number];
|
|
151
|
+
|
|
139
152
|
/** Upper bound on the number of ordered vision-handoff models. */
|
|
140
153
|
export const VISION_HANDOFF_MAX_MODELS = 8;
|
|
141
154
|
|
|
@@ -272,4 +285,9 @@ export const runtimeUpdateBody = t.Object({
|
|
|
272
285
|
t.Union([t.Null(), t.Number({ minimum: 64 * 1024, maximum: 100 * 1024 * 1024 })]),
|
|
273
286
|
),
|
|
274
287
|
visionHandoffModels: t.Optional(t.Array(t.String())),
|
|
288
|
+
autoDisableRateLimited: t.Optional(t.Boolean()),
|
|
289
|
+
modelProbeMode: t.Optional(literalUnion(MODEL_PROBE_MODES)),
|
|
290
|
+
modelProbeFastTimeoutSeconds: t.Optional(
|
|
291
|
+
t.Union([t.Null(), t.Number({ minimum: 1, maximum: 120 })]),
|
|
292
|
+
),
|
|
275
293
|
});
|
|
@@ -10,6 +10,7 @@ import { resolveUpstreamTimeoutMs } from "../../config/timeouts";
|
|
|
10
10
|
import { payloadRetentionMs } from "../../observability/payload-capture";
|
|
11
11
|
import { IMAGE_CLAMP_MAX_BYTES } from "../../protocol/image-clamp";
|
|
12
12
|
import {
|
|
13
|
+
MODEL_PROBE_MODES,
|
|
13
14
|
PONYTAIL_LEVELS,
|
|
14
15
|
RESPONSES_REASONING_SUMMARIES,
|
|
15
16
|
RTK_LEVELS,
|
|
@@ -23,6 +24,7 @@ import {
|
|
|
23
24
|
type RuntimeSettingsStore,
|
|
24
25
|
type TelemetryPayloadDepth,
|
|
25
26
|
type TelemetryPayloadMode,
|
|
27
|
+
type ModelProbeMode,
|
|
26
28
|
type UpdateRuntimeSettingsRequest,
|
|
27
29
|
} from "./contracts";
|
|
28
30
|
|
|
@@ -116,6 +118,23 @@ function normalizeConcurrencyTolerance(value: unknown): number | null {
|
|
|
116
118
|
return normalizeBoundedNumber(value, 0, 1024);
|
|
117
119
|
}
|
|
118
120
|
|
|
121
|
+
/** Modelprobe mode: only the two known modes survive; anything else = `detail`. */
|
|
122
|
+
function normalizeModelProbeMode(value: unknown): ModelProbeMode {
|
|
123
|
+
return typeof value === "string" && (MODEL_PROBE_MODES as readonly string[]).includes(value)
|
|
124
|
+
? (value as ModelProbeMode)
|
|
125
|
+
: "detail";
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Fast-probe timeout in seconds: 1–120, `null` = deployment default, and any
|
|
130
|
+
* other absent/non-finite value reads as the built-in default of 10 (the UI
|
|
131
|
+
* select's middle choice, and the value probing uses when unset).
|
|
132
|
+
*/
|
|
133
|
+
function normalizeModelProbeFastTimeoutSeconds(value: unknown): number | null {
|
|
134
|
+
if (value === null) return null;
|
|
135
|
+
return normalizeBoundedNumber(value, 1, 120) ?? 10;
|
|
136
|
+
}
|
|
137
|
+
|
|
119
138
|
/** Ordered vision-handoff models: non-empty strings, trimmed, de-duplicated. */
|
|
120
139
|
function normalizeVisionHandoffModels(value: unknown): readonly string[] {
|
|
121
140
|
if (!Array.isArray(value)) return [];
|
|
@@ -166,6 +185,11 @@ function mapRuntimeSettingsRow(
|
|
|
166
185
|
telemetryWindowMinutes: normalizeTelemetryWindowMinutes(prefs.telemetryWindowMinutes),
|
|
167
186
|
imageClampMaxBytes: normalizeImageClampMaxBytes(prefs.imageClampMaxBytes),
|
|
168
187
|
visionHandoffModels: normalizeVisionHandoffModels(prefs.visionHandoffModels),
|
|
188
|
+
autoDisableRateLimited: prefs.autoDisableRateLimited === true,
|
|
189
|
+
modelProbeMode: normalizeModelProbeMode(prefs.modelProbeMode),
|
|
190
|
+
modelProbeFastTimeoutSeconds: normalizeModelProbeFastTimeoutSeconds(
|
|
191
|
+
prefs.modelProbeFastTimeoutSeconds,
|
|
192
|
+
),
|
|
169
193
|
updatedAt,
|
|
170
194
|
/*
|
|
171
195
|
* Resolved at request time, not at module load: all four come from env the
|
|
@@ -231,6 +255,14 @@ export class DrizzleRuntimeSettingsStore implements RuntimeSettingsStore {
|
|
|
231
255
|
patchPrefs.imageClampMaxBytes = normalizeImageClampMaxBytes(patch.imageClampMaxBytes);
|
|
232
256
|
if (patch.visionHandoffModels !== undefined)
|
|
233
257
|
patchPrefs.visionHandoffModels = normalizeVisionHandoffModels(patch.visionHandoffModels);
|
|
258
|
+
if (patch.autoDisableRateLimited !== undefined)
|
|
259
|
+
patchPrefs.autoDisableRateLimited = patch.autoDisableRateLimited === true;
|
|
260
|
+
if (patch.modelProbeMode !== undefined)
|
|
261
|
+
patchPrefs.modelProbeMode = normalizeModelProbeMode(patch.modelProbeMode);
|
|
262
|
+
if (patch.modelProbeFastTimeoutSeconds !== undefined)
|
|
263
|
+
patchPrefs.modelProbeFastTimeoutSeconds = normalizeModelProbeFastTimeoutSeconds(
|
|
264
|
+
patch.modelProbeFastTimeoutSeconds,
|
|
265
|
+
);
|
|
234
266
|
|
|
235
267
|
const rows = await this.db
|
|
236
268
|
.insert(consoleSettings)
|
|
@@ -115,7 +115,6 @@ export const SYSTEM_PROMPT_ALIAS_LIMIT = 512;
|
|
|
115
115
|
const WRAPPED_INJECTION_PROVIDERS: ReadonlySet<string> = new Set([
|
|
116
116
|
"cb", // CodeBuddy International
|
|
117
117
|
"cbcn", // CodeBuddy CN
|
|
118
|
-
"workbuddy",
|
|
119
118
|
]);
|
|
120
119
|
|
|
121
120
|
/** Whether a provider's tenant text rides wrapped after the fixed identity prompt. */
|
package/src/main.ts
CHANGED
|
@@ -84,6 +84,7 @@ const app = boot
|
|
|
84
84
|
oauthRefreshService: boot.deps.oauthRefreshService,
|
|
85
85
|
admissionService: boot.deps.admissionService,
|
|
86
86
|
readRoutingAccountInflight: boot.deps.readRoutingAccountInflight,
|
|
87
|
+
ipAbuseStore: boot.deps.ipAbuseStore,
|
|
87
88
|
// The same three writers the shutdown coordinator drains, so a manual
|
|
88
89
|
// flush from the console does exactly what a graceful stop would do
|
|
89
90
|
// for the queues, minus the stop.
|
|
@@ -41,11 +41,26 @@ export const IS_GATEWAY_ERROR_SQL = gatewayErrorSql(telemetryEvents.status, tele
|
|
|
41
41
|
* cannot drift; the sum fragments coalesce to 0 so an empty window reads as
|
|
42
42
|
* zero rather than null.
|
|
43
43
|
*/
|
|
44
|
+
/**
|
|
45
|
+
* Cached-token aggregate under the "eligible request" rule.
|
|
46
|
+
*
|
|
47
|
+
* A request that carried any cached input (`cached_input_tokens > 0`) is a
|
|
48
|
+
* cache hit, and the WHOLE prompt is read as cached — the operator's rule is
|
|
49
|
+
* binary per request, not proportional (see the Usage page's "Cached tokens"
|
|
50
|
+
* tile and the provider Activity Matrix). So the aggregate is the sum of
|
|
51
|
+
* `input_tokens` over rows with a positive cached count, not the sum of the
|
|
52
|
+
* cached counts themselves.
|
|
53
|
+
*
|
|
54
|
+
* This reuses the same column the row-level estimator writes
|
|
55
|
+
* (`cached_input_tokens`), so it stays consistent across SQLite and Postgres.
|
|
56
|
+
*/
|
|
57
|
+
const CACHED_INPUT_TOKENS_SQL = sql<number>`coalesce(sum(case when ${telemetryEvents.cachedInputTokens} > 0 then ${telemetryEvents.inputTokens} else 0 end), 0)`;
|
|
58
|
+
|
|
44
59
|
const AGGREGATE_SELECT = {
|
|
45
60
|
errors: sql<number>`sum(case when ${IS_GATEWAY_ERROR_SQL} then 1 else 0 end)`,
|
|
46
61
|
inputTokens: sql<number>`coalesce(sum(${telemetryEvents.inputTokens}), 0)`,
|
|
47
62
|
outputTokens: sql<number>`coalesce(sum(${telemetryEvents.outputTokens}), 0)`,
|
|
48
|
-
cachedInputTokens:
|
|
63
|
+
cachedInputTokens: CACHED_INPUT_TOKENS_SQL,
|
|
49
64
|
} as const;
|
|
50
65
|
|
|
51
66
|
/** One bounded page of events, newest first. */
|
|
@@ -531,7 +546,6 @@ export async function breakdownTelemetry(
|
|
|
531
546
|
key: column,
|
|
532
547
|
requests: sql<number>`count(*)`,
|
|
533
548
|
...AGGREGATE_SELECT,
|
|
534
|
-
cachedInputTokens: sql<number>`coalesce(sum(${telemetryEvents.cachedInputTokens}), 0)`,
|
|
535
549
|
costUsd: sql<number>`coalesce(sum(${telemetryEvents.estimatedCostUsd}), 0)`,
|
|
536
550
|
avgTokensPerSec: sql<number>`coalesce(avg(${telemetryEvents.tokensPerSec}), 0)`,
|
|
537
551
|
})
|
|
@@ -620,7 +634,7 @@ export async function providerUsageMatrix(
|
|
|
620
634
|
model: telemetryEvents.requestedModel,
|
|
621
635
|
requests: sql<number>`count(*)`,
|
|
622
636
|
inputTokens: AGGREGATE_SELECT.inputTokens,
|
|
623
|
-
cachedInputTokens:
|
|
637
|
+
cachedInputTokens: CACHED_INPUT_TOKENS_SQL,
|
|
624
638
|
outputTokens: AGGREGATE_SELECT.outputTokens,
|
|
625
639
|
costUsd: sql<number>`coalesce(sum(${telemetryEvents.estimatedCostUsd}), 0)`,
|
|
626
640
|
})
|
|
@@ -861,7 +875,7 @@ export async function summarizeUsageWithPerformance(
|
|
|
861
875
|
truncated: sql<number>`sum(case when ${telemetryEvents.status} = 'truncated' then 1 else 0 end)`,
|
|
862
876
|
inputTokens: AGGREGATE_SELECT.inputTokens,
|
|
863
877
|
outputTokens: AGGREGATE_SELECT.outputTokens,
|
|
864
|
-
cachedInputTokens:
|
|
878
|
+
cachedInputTokens: CACHED_INPUT_TOKENS_SQL,
|
|
865
879
|
reasoningTokens: sql<number>`coalesce(sum(${telemetryEvents.reasoningTokens}), 0)`,
|
|
866
880
|
estimatedCostUsd: sql<number>`coalesce(sum(${telemetryEvents.estimatedCostUsd}), 0)`,
|
|
867
881
|
sampled: sql<number>`count(${telemetryEvents.latencyMs})`,
|
|
@@ -55,6 +55,18 @@ function hasSqliteDialect(database: unknown): boolean {
|
|
|
55
55
|
return (database as { session?: unknown } | undefined)?.session instanceof SQLiteBunSession;
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
+
/**
|
|
59
|
+
* Dialect of a concrete Drizzle handle, rather than of the process's env.
|
|
60
|
+
*
|
|
61
|
+
* A test can hold an in-memory SQLite database while `CARTETHYIA_DB_MODE`
|
|
62
|
+
* names `full` (the backend suite sets that globally). A raw SQL predicate
|
|
63
|
+
* chosen from the env would emit `jsonb_each_text` into SQLite and fail; the
|
|
64
|
+
* handle's session is the authority, exactly as `mergeJson` already treats it.
|
|
65
|
+
*/
|
|
66
|
+
export function isSqliteDatabase(database: unknown): boolean {
|
|
67
|
+
return hasSqliteDialect(database);
|
|
68
|
+
}
|
|
69
|
+
|
|
58
70
|
/**
|
|
59
71
|
* The per-model cooldown object with every entry whose timestamp has passed
|
|
60
72
|
* removed, or `'{}'` when nothing remains.
|
|
@@ -219,8 +219,8 @@ export interface NativeIdentityRecord {
|
|
|
219
219
|
readonly machineCode: string;
|
|
220
220
|
/** The seed-file content this identity was minted from, if captured. */
|
|
221
221
|
readonly seed?: string;
|
|
222
|
-
/** How this identity was produced: the genuine SG bridge or the fallback. */
|
|
223
|
-
readonly source?: "runtime-info" | "generated";
|
|
222
|
+
/** How this identity was produced: the genuine SG bridge, the OS fingerprint, or the fallback. */
|
|
223
|
+
readonly source?: "runtime-info" | "generated" | "os";
|
|
224
224
|
}
|
|
225
225
|
|
|
226
226
|
export const providerAccounts = pgTable("provider_accounts", {
|
|
@@ -432,6 +432,11 @@ export const models = pgTable(
|
|
|
432
432
|
reasoning: boolean("reasoning").notNull().default(false),
|
|
433
433
|
toolCall: boolean("tool_call").notNull().default(false),
|
|
434
434
|
cost: jsonb("cost"),
|
|
435
|
+
// Display-only warning metadata for this row: Postgres `jsonb` (SQLite
|
|
436
|
+
// `text` carrying the JSON string), nullable, `NULL` = no warnings. Shape:
|
|
437
|
+
// `{ warnings: [{ kind, message, severity?, updatedAt? }] }`. Part of the
|
|
438
|
+
// provider-account model-update proposal §3.4 area 06; not read by routing.
|
|
439
|
+
warningMetadata: jsonb("warning_metadata"),
|
|
435
440
|
source: text("source"),
|
|
436
441
|
sourceUpdatedAt: timestamp("source_updated_at", { withTimezone: true }),
|
|
437
442
|
enabled: boolean("enabled").notNull().default(true),
|
|
@@ -1288,6 +1293,19 @@ export interface ConsoleSettingsPreferences {
|
|
|
1288
1293
|
* placeholder behavior.
|
|
1289
1294
|
*/
|
|
1290
1295
|
visionHandoffModels?: readonly string[];
|
|
1296
|
+
/**
|
|
1297
|
+
* Auto-disable provider accounts that keep failing with upstream rate-limit
|
|
1298
|
+
* or quota verdicts. Off by default: without it, those failures land on a
|
|
1299
|
+
* per-model/account cooldown and the account stays rotatable.
|
|
1300
|
+
*/
|
|
1301
|
+
autoDisableRateLimited?: boolean;
|
|
1302
|
+
/**
|
|
1303
|
+
* Modelprobe mode: `detail` (full identity probe) or `fast` (minimal
|
|
1304
|
+
* 32-token check). `detail` when unset.
|
|
1305
|
+
*/
|
|
1306
|
+
modelProbeMode?: "fast" | "detail";
|
|
1307
|
+
/** Fast-probe timeout in seconds; `null`/absent means the built-in default (10). */
|
|
1308
|
+
modelProbeFastTimeoutSeconds?: number | null;
|
|
1291
1309
|
}
|
|
1292
1310
|
|
|
1293
1311
|
export const consoleSettings = pgTable("console_settings", {
|
package/src/protocol/dsml.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* markup inside the plain-text content stream instead of the structured
|
|
6
6
|
* `tool_calls` field. The gateway sees it from one upstream family:
|
|
7
7
|
*
|
|
8
|
-
* - CodeBuddy
|
|
8
|
+
* - CodeBuddy emits strict blocks (`<|DSML| calls>…</|DSML| calls>`)
|
|
9
9
|
* that must be parsed exactly, with declared-tool allowlisting.
|
|
10
10
|
*/
|
|
11
11
|
export interface DsmlToolCall {
|
|
@@ -34,7 +34,7 @@ export function applyOpenAIReasoning(payload: Record<string, unknown>, request:
|
|
|
34
34
|
payload["reasoning_effort"] = effort;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
/** Applies the DeepSeek chat contract shared by CodeBuddy
|
|
37
|
+
/** Applies the DeepSeek chat contract shared by CodeBuddy. */
|
|
38
38
|
export function applyDeepSeekReasoning(payload: Record<string, unknown>): void {
|
|
39
39
|
const model = typeof payload["model"] === "string" ? payload["model"].trim().toLowerCase() : "";
|
|
40
40
|
if (!model.startsWith("deepseek")) return;
|
|
@@ -248,7 +248,7 @@ export function canonicalToChatPayload(
|
|
|
248
248
|
// to be replayed, and the tool-call turn is exactly where a thinking
|
|
249
249
|
// model emits it — dropping it here made the next request fail with
|
|
250
250
|
// "the reasoning content from the previous turn must be passed back in
|
|
251
|
-
// thinking mode" (
|
|
251
|
+
// thinking mode" (CodeBuddy 400). Chat Completions has no
|
|
252
252
|
// reasoning content part, so it travels in the provider-native
|
|
253
253
|
// `reasoning_content` side channel, same as the branches below.
|
|
254
254
|
const reasoningContent = m.content
|
|
@@ -365,7 +365,7 @@ export function canonicalToChatPayload(
|
|
|
365
365
|
// nothing left to say: emitting it as an empty-content `user` message
|
|
366
366
|
// right after the tool results breaks strict upstreams (a bare `user`
|
|
367
367
|
// turn between a result and the next assistant turn reads as a broken
|
|
368
|
-
// tool sequence —
|
|
368
|
+
// tool sequence — CodeBuddy `11148`). Fold the leftover
|
|
369
369
|
// reasoning into the last tool message's side channel when one was
|
|
370
370
|
// just emitted, and skip the turn otherwise; a turn that still
|
|
371
371
|
// carries text or rich parts emits below as usual.
|
|
@@ -382,7 +382,7 @@ export function canonicalToChatPayload(
|
|
|
382
382
|
// or a non-empty serialised payload. Pushing `{content:"",
|
|
383
383
|
// reasoning_content:""}` is the opposite of load-bearing — the
|
|
384
384
|
// upstream reads it as "thinking mode with the reasoning stripped"
|
|
385
|
-
// and rejects the whole request (`
|
|
385
|
+
// and rejects the whole request (`CodeBuddy 11151 "history
|
|
386
386
|
// not found"`). The canonical form that reaches this branch with no
|
|
387
387
|
// trace is an input `reasoning` item whose `summary` was absent and
|
|
388
388
|
// whose `payload` was `null` — a signature-less thinking block
|
|
@@ -570,7 +570,7 @@ export function canonicalToChatPayload(
|
|
|
570
570
|
// Resolved unconditionally, not only when the caller sent a value: the bound
|
|
571
571
|
// table now carries a `defaultMaxTokens` for this wire, and an omitted limit
|
|
572
572
|
// must reach the upstream as a real number. Upstreams that require the field
|
|
573
|
-
// reject a large prompt whose `max_tokens` is absent outright (
|
|
573
|
+
// reject a large prompt whose `max_tokens` is absent outright (CodeBuddy
|
|
574
574
|
// `400 model_param_invalid`, reproduced through the gateway).
|
|
575
575
|
const bounded = resolveWireMaxTokens("chat", { max_tokens: requestedMaxTokens }, {
|
|
576
576
|
...(options.maxOutputTokens === undefined ? {} : { maxOutputTokens: options.maxOutputTokens }),
|
|
@@ -39,7 +39,7 @@ import { claudeResponseToEvents, parseClaudeSseStream } from "../response/messag
|
|
|
39
39
|
* Gateway default when the caller sent no max_tokens (Anthropic requires
|
|
40
40
|
* the field). Explicit and warned at the call site — never silent.
|
|
41
41
|
*/
|
|
42
|
-
const ANTHROPIC_DEFAULT_MAX_TOKENS = WIRE_TOKEN_BOUNDS["messages"]?.defaultMaxTokens ?? 4_096;
|
|
42
|
+
export const ANTHROPIC_DEFAULT_MAX_TOKENS = WIRE_TOKEN_BOUNDS["messages"]?.defaultMaxTokens ?? 4_096;
|
|
43
43
|
|
|
44
44
|
/** Anthropic Messages' documented base64-image media-type allowlist. */
|
|
45
45
|
const ANTHROPIC_IMAGE_MEDIA_TYPES = new Set([
|
|
@@ -38,15 +38,20 @@ export function responseParts(parts: readonly Record<string, unknown>[]): {
|
|
|
38
38
|
readonly text: string;
|
|
39
39
|
readonly calls: readonly { readonly id: string; readonly name: string; readonly args: Record<string, unknown> }[];
|
|
40
40
|
readonly thought: string;
|
|
41
|
+
readonly thoughtSignature: string;
|
|
41
42
|
} {
|
|
42
43
|
const text: string[] = [];
|
|
43
44
|
const thought: string[] = [];
|
|
45
|
+
let thoughtSignature = "";
|
|
44
46
|
const calls: Array<{ id: string; name: string; args: Record<string, unknown> }> = [];
|
|
45
47
|
let callIndex = 0;
|
|
46
48
|
for (const part of parts) {
|
|
47
49
|
if (typeof part["text"] === "string") {
|
|
48
50
|
if (part["thought"] === true) {
|
|
49
51
|
thought.push(part["text"] as string);
|
|
52
|
+
if (thoughtSignature.length === 0 && typeof part["thoughtSignature"] === "string") {
|
|
53
|
+
thoughtSignature = part["thoughtSignature"] as string;
|
|
54
|
+
}
|
|
50
55
|
} else text.push(part["text"] as string);
|
|
51
56
|
}
|
|
52
57
|
if (isRecord(part["functionCall"]) && typeof (part["functionCall"] as Record<string, unknown>)["name"] === "string") {
|
|
@@ -58,7 +63,7 @@ export function responseParts(parts: readonly Record<string, unknown>[]): {
|
|
|
58
63
|
});
|
|
59
64
|
}
|
|
60
65
|
}
|
|
61
|
-
return { text: text.join(""), calls, thought: thought.join("") };
|
|
66
|
+
return { text: text.join(""), calls, thought: thought.join(""), thoughtSignature };
|
|
62
67
|
}
|
|
63
68
|
|
|
64
69
|
export function mapGeminiUsage(body: Record<string, unknown>): Record<string, unknown> {
|
|
@@ -93,8 +93,19 @@ function isCompleteJsonValue(data: string): boolean {
|
|
|
93
93
|
// invited a retry of the identical request against a broken upstream. The
|
|
94
94
|
// `upstream` origin keeps the public envelope from prefixing it
|
|
95
95
|
// a gateway-branded message, which blamed the gateway for the provider's bytes.
|
|
96
|
-
|
|
97
|
-
|
|
96
|
+
// The message quotes the offending line (bounded) so the client and operator
|
|
97
|
+
// see the actual bytes the upstream sent rather than a generic sentence.
|
|
98
|
+
const MAX_SSE_SNIPPET = 160;
|
|
99
|
+
function snippet(line: string): string {
|
|
100
|
+
const collapsed = line.replace(/[\r\n]+/g, " ").trim();
|
|
101
|
+
if (collapsed.length === 0) return "<empty line>";
|
|
102
|
+
return collapsed.length > MAX_SSE_SNIPPET
|
|
103
|
+
? `${collapsed.slice(0, MAX_SSE_SNIPPET)}…`
|
|
104
|
+
: collapsed;
|
|
105
|
+
}
|
|
106
|
+
function oversized(what: string, line?: string): GatewayError {
|
|
107
|
+
const quote = line === undefined ? "" : `: ${snippet(line)}`;
|
|
108
|
+
return new GatewayError("platform_unavailable", 502, `SSE ${what} exceeds size bound${quote} (request {requestId}). The upstream sent more than we can hold — retry 🌙`, {}, "upstream");
|
|
98
109
|
}
|
|
99
110
|
|
|
100
111
|
export async function* decodeSseEvents(
|
|
@@ -161,7 +172,7 @@ export async function* decodeSseEvents(
|
|
|
161
172
|
const rest = last.subarray(newlineAt + 1);
|
|
162
173
|
if (rest.byteLength > 0) pending.unshift(rest);
|
|
163
174
|
const line = decoder.decode(lineBytes).replace(/\r$/, "");
|
|
164
|
-
if (line.length > maxLineBytes) throw oversized("line");
|
|
175
|
+
if (line.length > maxLineBytes) throw oversized("line", line);
|
|
165
176
|
return line;
|
|
166
177
|
}
|
|
167
178
|
function* dispatchEvent(): Iterable<SseEvent> {
|
|
@@ -188,7 +199,7 @@ export async function* decodeSseEvents(
|
|
|
188
199
|
const piece = line.slice(5).trimStart();
|
|
189
200
|
dataLines.push(piece);
|
|
190
201
|
eventBytes += piece.length;
|
|
191
|
-
if (eventBytes > maxEventBytes) throw oversized("event");
|
|
202
|
+
if (eventBytes > maxEventBytes) throw oversized("event", line);
|
|
192
203
|
return;
|
|
193
204
|
}
|
|
194
205
|
if (line.startsWith("id:") || line.startsWith("retry:")) return;
|
|
@@ -220,10 +231,10 @@ export async function* decodeSseEvents(
|
|
|
220
231
|
if (dataLines.length > 0) {
|
|
221
232
|
dataLines.push(line);
|
|
222
233
|
eventBytes += line.length;
|
|
223
|
-
if (eventBytes > maxEventBytes) throw oversized("event");
|
|
234
|
+
if (eventBytes > maxEventBytes) throw oversized("event", line);
|
|
224
235
|
return;
|
|
225
236
|
}
|
|
226
|
-
throw new GatewayError("platform_unavailable", 502,
|
|
237
|
+
throw new GatewayError("platform_unavailable", 502, `Malformed SSE line: ${snippet(line)} (request {requestId}). The upstream sent something we can't read — retry 🌙`, {}, "upstream");
|
|
227
238
|
}
|
|
228
239
|
|
|
229
240
|
try {
|
|
@@ -233,7 +244,10 @@ export async function* decodeSseEvents(
|
|
|
233
244
|
if (chunk.value && chunk.value.byteLength > 0) {
|
|
234
245
|
pending.push(chunk.value);
|
|
235
246
|
pendingBytes += chunk.value.byteLength;
|
|
236
|
-
if (!hasPendingNewline() && pendingBytes > maxLineBytes)
|
|
247
|
+
if (!hasPendingNewline() && pendingBytes > maxLineBytes) {
|
|
248
|
+
const partial = decoder.decode(new Uint8Array(pendingBytes)).slice(0, MAX_SSE_SNIPPET);
|
|
249
|
+
throw oversized("line", partial);
|
|
250
|
+
}
|
|
237
251
|
}
|
|
238
252
|
let line = takeLine();
|
|
239
253
|
while (line !== undefined) {
|
|
@@ -243,7 +257,10 @@ export async function* decodeSseEvents(
|
|
|
243
257
|
if (chunk.done) break;
|
|
244
258
|
}
|
|
245
259
|
if (pendingBytes > 0) {
|
|
246
|
-
if (pendingBytes > maxLineBytes)
|
|
260
|
+
if (pendingBytes > maxLineBytes) {
|
|
261
|
+
const partial = decoder.decode(new Uint8Array(pendingBytes)).slice(0, MAX_SSE_SNIPPET);
|
|
262
|
+
throw oversized("line", partial);
|
|
263
|
+
}
|
|
247
264
|
const total = new Uint8Array(pendingBytes);
|
|
248
265
|
let offset = 0;
|
|
249
266
|
for (const chunk of pending.splice(0, pending.length)) {
|
|
@@ -17,7 +17,11 @@
|
|
|
17
17
|
* which is honest: the upstream failed and we do not know more.
|
|
18
18
|
*/
|
|
19
19
|
import { GatewayError } from "./gateway-error";
|
|
20
|
-
import {
|
|
20
|
+
import {
|
|
21
|
+
classifyUpstreamError,
|
|
22
|
+
upstreamErrorIdentifier,
|
|
23
|
+
extractUpstreamMessageText,
|
|
24
|
+
} from "../transport/failure-policy";
|
|
21
25
|
import { isRecord } from "./codecs";
|
|
22
26
|
|
|
23
27
|
|
|
@@ -54,19 +58,25 @@ export function gatewayErrorFromStreamError(
|
|
|
54
58
|
}
|
|
55
59
|
if (!isRecord(error)) return undefined;
|
|
56
60
|
|
|
57
|
-
const message = extractUpstreamMessage(error);
|
|
58
61
|
const identifier = upstreamErrorIdentifier(error);
|
|
59
62
|
const explicitStatus = numericStatus(error["status"], error["statusCode"], error["status_code"]);
|
|
60
63
|
const classification = classifyUpstreamError(explicitStatus, identifier);
|
|
61
64
|
const raw = JSON.stringify(error, null, 2).slice(0, 16_384);
|
|
65
|
+
// Only genuine prose (`message`/`msg`/`error` fields) counts as upstream
|
|
66
|
+
// passthrough text. A `code`/identifier is a classification key, not prose —
|
|
67
|
+
// it still serves as the message fallback when no text exists, but it must
|
|
68
|
+
// not set `upstreamMessageAsIs`.
|
|
69
|
+
const prose = extractUpstreamMessageText(error);
|
|
70
|
+
const message = prose.slice(0, 500) || identifier || label;
|
|
62
71
|
return new GatewayError(
|
|
63
72
|
classification.code,
|
|
64
73
|
classification.status,
|
|
65
|
-
message
|
|
74
|
+
message,
|
|
66
75
|
{
|
|
67
76
|
...(explicitStatus === undefined ? {} : { providerStatus: explicitStatus }),
|
|
68
77
|
...(identifier ? { providerCode: identifier } : {}),
|
|
69
78
|
raw,
|
|
79
|
+
...(prose ? { upstreamMessageAsIs: true } : {}),
|
|
70
80
|
...(classification.credentialEvidence ? { credentialEvidence: true } : {}),
|
|
71
81
|
...(classification.rateLimitScope
|
|
72
82
|
? { rateLimitScope: classification.rateLimitScope }
|
|
@@ -49,7 +49,7 @@ interface WireTokenBound {
|
|
|
49
49
|
* The output limit assumed when the caller states none, for wires that need
|
|
50
50
|
* the field present.
|
|
51
51
|
*
|
|
52
|
-
* PROVEN, not assumed: a 3.16M-char prompt to
|
|
52
|
+
* PROVEN, not assumed: a 3.16M-char prompt to CodeBuddy
|
|
53
53
|
* (`bansos/deepseek-v4.1-flash`) answered `400 model_param_invalid` whenever
|
|
54
54
|
* `max_tokens` was absent, and answered 200 with the *same* body plus any
|
|
55
55
|
* explicit value — `1`, `16`, `1000`, `8000`, `64000`, `384000` all passed.
|
|
@@ -91,7 +91,7 @@ export function repairMissingToolResponses(
|
|
|
91
91
|
* fully answered by matching `tool` results is stripped of its calls (kept when
|
|
92
92
|
* it still carries content), and the dangling results go with it.
|
|
93
93
|
*
|
|
94
|
-
* This is the buddy-family policy (
|
|
94
|
+
* This is the buddy-family policy (CodeBuddy code `11148`): a partial
|
|
95
95
|
* batch `assistant[c1 c2] + tool[c1]` is a broken sequence the upstream rejects,
|
|
96
96
|
* and a synthesized placeholder cannot stand in for a real execution the model
|
|
97
97
|
* never saw. Contrast `repairMissingToolResponses`, which synthesizes an
|
|
@@ -188,7 +188,7 @@ export function repairRequestToolCalls(request: CanonicalRequest): CanonicalRequ
|
|
|
188
188
|
* True when a `toolResult` part is a `tool`-role turn whose `toolCall` is
|
|
189
189
|
* absent from the whole history, or when results for one batch of calls are
|
|
190
190
|
* not contiguous. Upstreams reject both with `tool_call_sequence_broken`
|
|
191
|
-
* (
|
|
191
|
+
* (CodeBuddy code `11148`), and a broken history stays broken: the
|
|
192
192
|
* client replays it on every later turn, so the whole conversation dies.
|
|
193
193
|
*/
|
|
194
194
|
function toolCallIdsIn(message: CanonicalMessage): string[] {
|