cartethyia 2.7.8 → 2.8.0
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 +3 -3
- package/README.md +29 -59
- package/dist/dashboard/assets/BannedPage-Cc0vKpMG.js +1 -0
- package/dist/dashboard/assets/BannedPage-Cc0vKpMG.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolDetailPage-BRWKPQPf.js → CliToolDetailPage-Bqj2aa1T.js} +1 -1
- package/dist/dashboard/assets/CliToolDetailPage-Bqj2aa1T.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolsPage-BP-Y5iHN.js → CliToolsPage-CXsaOAO2.js} +1 -1
- package/dist/dashboard/assets/CliToolsPage-CXsaOAO2.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-BeLpJ4ma.js +1 -0
- package/dist/dashboard/assets/CombosPage-BeLpJ4ma.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-DnV0uZ3m.js +1 -0
- package/dist/dashboard/assets/ConsoleLogPage-DnV0uZ3m.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-CBxAjo63.js +1 -0
- package/dist/dashboard/assets/CustomizationPage-CBxAjo63.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-B5yEE0k6.js +1 -0
- package/dist/dashboard/assets/HealthEventsModal-B5yEE0k6.js.gz +0 -0
- package/dist/dashboard/assets/{InjectSystemPromptPanel-Cv-2Ol_K.js → InjectSystemPromptPanel-BWFLECmz.js} +2 -2
- package/dist/dashboard/assets/InjectSystemPromptPanel-BWFLECmz.js.gz +0 -0
- package/dist/dashboard/assets/{LoginPage-bKfz_b1K.js → LoginPage-CgoPc5B9.js} +1 -1
- package/dist/dashboard/assets/LoginPage-CgoPc5B9.js.gz +0 -0
- package/dist/dashboard/assets/{ModelPicker-B-WQxx_H.js → ModelPicker-D9bFQern.js} +1 -1
- package/dist/dashboard/assets/ModelPicker-D9bFQern.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-B_p4tL8k.js +1 -0
- package/dist/dashboard/assets/OverviewPage-B_p4tL8k.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-rs5E2rxv.js +8 -0
- package/dist/dashboard/assets/ProviderDetailPage-rs5E2rxv.js.gz +0 -0
- package/dist/dashboard/assets/ProviderIcon-D_0PiZtg.js +1 -0
- package/dist/dashboard/assets/ProviderIcon-D_0PiZtg.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-CLI2bJTK.js +1 -0
- package/dist/dashboard/assets/ProvidersPage-CLI2bJTK.js.gz +0 -0
- package/dist/dashboard/assets/{ProxyPage-emlTuxz6.js → ProxyPage-BwNC_-Cx.js} +1 -1
- package/dist/dashboard/assets/ProxyPage-BwNC_-Cx.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-DA-a1iSN.js +1 -0
- package/dist/dashboard/assets/QuotaPage-DA-a1iSN.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-CctTga2C.js +1 -0
- package/dist/dashboard/assets/SettingsPage-CctTga2C.js.gz +0 -0
- package/dist/dashboard/assets/{SetupPage-CUZrKs9r.js → SetupPage-febLU4r1.js} +1 -1
- package/dist/dashboard/assets/SetupPage-febLU4r1.js.gz +0 -0
- package/dist/dashboard/assets/{StudioPage-jWmUdDpy.js → StudioPage-M6fvR5Wz.js} +1 -1
- package/dist/dashboard/assets/StudioPage-M6fvR5Wz.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-nyeuRqz9.js +56 -0
- package/dist/dashboard/assets/UsagePage-nyeuRqz9.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-BFOlvrJC.js +1 -0
- package/dist/dashboard/assets/api-keys-BFOlvrJC.js.gz +0 -0
- package/dist/dashboard/assets/app-Dx2QgsNj.css +2 -0
- package/dist/dashboard/assets/app-Dx2QgsNj.css.gz +0 -0
- package/dist/dashboard/assets/{assert-CidQVoNo.js → assert-CqTLw5e7.js} +1 -1
- package/dist/dashboard/assets/assert-CqTLw5e7.js.gz +0 -0
- package/dist/dashboard/assets/{badge-DyI_mJfi.js → badge-C83EqLB0.js} +1 -1
- package/dist/dashboard/assets/badge-C83EqLB0.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-D1xn7pIe.js} +1 -1
- package/dist/dashboard/assets/banner-D1xn7pIe.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-DU3Lq5Lm.js} +1 -1
- package/dist/dashboard/assets/card-DU3Lq5Lm.js.gz +0 -0
- package/dist/dashboard/assets/cli-tools-Dw1U_drK.js +1 -0
- package/dist/dashboard/assets/cli-tools-Dw1U_drK.js.gz +0 -0
- package/dist/dashboard/assets/{clipboard-button-ByjCk0o9.js → clipboard-button-CEL5Sg_J.js} +1 -1
- package/dist/dashboard/assets/clipboard-button-CEL5Sg_J.js.gz +0 -0
- package/dist/dashboard/assets/{dialog-BZhOlfz-.js → dialog-BjFR5UHy.js} +1 -1
- package/dist/dashboard/assets/dialog-BjFR5UHy.js.gz +0 -0
- package/dist/dashboard/assets/{drawer-BADqIRYP.js → drawer-HvBW0AjI.js} +1 -1
- package/dist/dashboard/assets/drawer-HvBW0AjI.js.gz +0 -0
- package/dist/dashboard/assets/entry-BSu3gFlt.js +4 -0
- package/dist/dashboard/assets/entry-BSu3gFlt.js.gz +0 -0
- package/dist/dashboard/assets/{entry-COaC-irN.js → entry-CECVELyy.js} +1 -1
- package/dist/dashboard/assets/entry-CECVELyy.js.gz +0 -0
- package/dist/dashboard/assets/entry-DYYofQ7W.js +1 -0
- package/dist/dashboard/assets/entry-DYYofQ7W.js.gz +0 -0
- package/dist/dashboard/assets/{formatters-DcHJyRiu.js → formatters-DLW4i8zK.js} +1 -1
- package/dist/dashboard/assets/formatters-DLW4i8zK.js.gz +0 -0
- package/dist/dashboard/assets/{index-CgKmFngv.js → index-1-fCqe64.js} +2 -2
- package/dist/dashboard/assets/index-1-fCqe64.js.gz +0 -0
- package/dist/dashboard/assets/{inline-B2BirKry.js → inline-DGvU2oLK.js} +1 -1
- package/dist/dashboard/assets/inline-DGvU2oLK.js.gz +0 -0
- package/dist/dashboard/assets/{input-D-qV9dKY.js → input-iNnO1X2K.js} +1 -1
- package/dist/dashboard/assets/input-iNnO1X2K.js.gz +0 -0
- package/dist/dashboard/assets/{layout-B7tVWhtV.js → layout-0q8UGypb.js} +1 -1
- package/dist/dashboard/assets/layout-0q8UGypb.js.gz +0 -0
- package/dist/dashboard/assets/{list-row-CtkGE3bF.js → list-row-C0w2bQjJ.js} +1 -1
- package/dist/dashboard/assets/list-row-C0w2bQjJ.js.gz +0 -0
- package/dist/dashboard/assets/{live-DGizF06-.js → live-M5aJ_xCa.js} +1 -1
- package/dist/dashboard/assets/live-M5aJ_xCa.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-BKeQGT3K.js} +1 -1
- package/dist/dashboard/assets/network-BKeQGT3K.js.gz +0 -0
- package/dist/dashboard/assets/{numbers-BnAd22a_.js → numbers-CivAk3da.js} +1 -1
- package/dist/dashboard/assets/numbers-CivAk3da.js.gz +0 -0
- package/dist/dashboard/assets/{pagination-BAywyMzS.js → pagination-CpzvI5ep.js} +1 -1
- package/dist/dashboard/assets/pagination-CpzvI5ep.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/providers/zro.svg +3 -0
- package/dist/dashboard/assets/providers/zro.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/quota-DiPD6UKH.js +1 -0
- package/dist/dashboard/assets/quota-DiPD6UKH.js.gz +0 -0
- package/dist/dashboard/assets/routing-BFQX2VBc.js +1 -0
- package/dist/dashboard/assets/routing-BFQX2VBc.js.gz +0 -0
- package/dist/dashboard/assets/{select-BCOcv8WK.js → select-CXbck0jG.js} +2 -2
- package/dist/dashboard/assets/select-CXbck0jG.js.gz +0 -0
- package/dist/dashboard/assets/settings-C_XvMHh9.js +1 -0
- package/dist/dashboard/assets/settings-C_XvMHh9.js.gz +0 -0
- package/dist/dashboard/assets/{stack-ByKt3Q1g.js → stack-DggRiCxE.js} +1 -1
- package/dist/dashboard/assets/stack-DggRiCxE.js.gz +0 -0
- package/dist/dashboard/assets/{state-DXbGTx7N.js → state-ClFzfbYp.js} +1 -1
- package/dist/dashboard/assets/state-ClFzfbYp.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-CTIFxKqS.js} +1 -1
- package/dist/dashboard/assets/switch-CTIFxKqS.js.gz +0 -0
- package/dist/dashboard/assets/{useMutation-CbCP4w1o.js → useMutation-BClG3yWj.js} +1 -1
- package/dist/dashboard/assets/useMutation-BClG3yWj.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 +59 -56
- 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/full-postgres/0046_add_accounts_quota_plan_fetched_at.sql +7 -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/migrations/lite-sqlite3/0046_add_accounts_quota_plan_fetched_at.sql +4 -0
- package/package.json +1 -1
- package/src/boot/app.ts +10 -13
- package/src/boot/dependencies.ts +12 -4
- package/src/boot/flush.ts +20 -0
- package/src/boot/lifecycle.ts +7 -5
- package/src/config/cooldowns.ts +1 -1
- package/src/config/routing.ts +26 -0
- package/src/config/spec-metadata.ts +72 -0
- package/src/config/spec.ts +20 -6
- package/src/console/api-keys/contracts.ts +12 -1
- package/src/console/api-keys/routes.ts +50 -31
- package/src/console/auth/account-reset.ts +7 -9
- package/src/console/backup/contracts.ts +4 -0
- package/src/console/backup/export-endpoint.ts +7 -5
- package/src/console/cli-tools/service.ts +6 -12
- package/src/console/console-router-guards.ts +23 -16
- package/src/console/console-router.ts +7 -0
- package/src/console/dashboard-assets.ts +5 -5
- package/src/console/domain-registration.ts +27 -2
- package/src/console/inferhub/normalize.ts +24 -28
- package/src/console/inferhub/types.ts +3 -0
- package/src/console/ip-bans/routes.ts +93 -0
- package/src/console/observability/store.ts +0 -53
- package/src/console/providers/catalog/catalog-projections.ts +5 -2
- package/src/console/providers/catalog/contracts.ts +123 -0
- package/src/console/providers/catalog/discover-constants.ts +6 -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 +65 -4
- package/src/console/providers/catalog/store-helpers.ts +30 -1
- package/src/console/providers/catalog/store.ts +249 -4
- package/src/console/quota/account-quota-global.ts +97 -19
- package/src/console/quota/account-quota-shared.ts +49 -0
- package/src/console/quota/account-quota-tenant.ts +6 -18
- package/src/console/quota/native-identity.ts +2 -0
- package/src/console/quota/quota-view.ts +19 -3
- package/src/console/routing/pools/operations.ts +31 -46
- package/src/console/settings/contracts.ts +18 -0
- package/src/console/settings/store.ts +32 -0
- package/src/console/shared/errors.ts +33 -6
- package/src/console/system-prompt/contracts.ts +0 -1
- package/src/main.ts +3 -3
- package/src/network/pool/byte-accounting.ts +3 -2
- package/src/network/pool/selector.ts +182 -39
- package/src/network/pool-cooldown-store.ts +89 -14
- package/src/network/pool-health.ts +49 -61
- package/src/network/rate-limit-verdict.ts +1 -1
- package/src/network/ssrf.ts +6 -6
- package/src/observability/event-log.ts +7 -361
- package/src/observability/event-query.ts +18 -4
- package/src/observability/performance-metrics.ts +9 -0
- package/src/observability/runtime-metrics.ts +14 -9
- package/src/observability/telemetry-buffer.ts +15 -25
- package/src/observability/telemetry-reconciliation.ts +9 -37
- package/src/observability/telemetry-row.ts +125 -0
- package/src/persistence/db-mode.ts +4 -1
- package/src/persistence/dialect.ts +30 -0
- package/src/persistence/migrate.ts +6 -3
- package/src/persistence/schema.ts +28 -2
- package/src/persistence/ttl-cache.ts +20 -2
- package/src/protocol/codecs.ts +1 -1
- package/src/protocol/dsml.ts +35 -87
- package/src/protocol/gateway-error.ts +1 -0
- package/src/protocol/markup-stream-parser.ts +195 -0
- package/src/protocol/reasoning-text.ts +19 -0
- package/src/protocol/reasoning.ts +1 -1
- package/src/protocol/request/chat.ts +14 -14
- package/src/protocol/request/gemini.ts +8 -2
- package/src/protocol/request/messages.ts +4 -5
- package/src/protocol/response/chat.ts +12 -20
- package/src/protocol/response/gemini.ts +6 -1
- package/src/protocol/response/omp-tool-call-markup.ts +43 -86
- package/src/protocol/response/responses.ts +18 -2
- package/src/protocol/sse-decoder.ts +25 -8
- package/src/protocol/stop-reason.ts +23 -1
- package/src/protocol/stream-error-frames.ts +13 -3
- package/src/protocol/think-split.ts +112 -0
- package/src/protocol/translation/dialect/qwen.ts +26 -58
- 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 +78 -17
- package/src/providers/authentication/oauth-client.ts +1 -1
- package/src/providers/authentication/oauth-device-flow.ts +98 -8
- package/src/providers/authentication/oauth-refresh-service.ts +153 -59
- package/src/providers/configured-provider.ts +148 -0
- package/src/providers/default-registry.ts +117 -27
- package/src/providers/discovery/discovery-types.ts +12 -0
- package/src/providers/discovery/probe-phases.ts +74 -33
- package/src/providers/discovery/probing-service.ts +293 -165
- package/src/providers/integrations/alibabacp/alibabacp-models.ts +1 -2
- package/src/providers/integrations/{dashscope-models.ts → alibabacp/dashscope-models.ts} +2 -2
- package/src/providers/integrations/antigravity/antigravity-protocol.ts +121 -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 +191 -35
- 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 +89 -6
- 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 +6 -43
- package/src/providers/integrations/buddy/codebuddy/cn-catalog.ts +2 -1
- package/src/providers/integrations/buddy/codebuddy/cn-provider.ts +23 -1
- package/src/providers/integrations/buddy/codebuddy/provider.ts +6 -2
- package/src/providers/integrations/buddy/codebuddy/shared.ts +38 -27
- package/src/providers/integrations/claude/claude-quota.ts +14 -17
- package/src/providers/integrations/cline/cline.ts +2 -2
- package/src/providers/integrations/commandcode/quota.ts +22 -28
- package/src/providers/integrations/cursor/cursor-auth.ts +0 -8
- package/src/providers/integrations/cursor/cursor-catalog.ts +2 -0
- package/src/providers/integrations/cursor/cursor-exec.ts +745 -0
- package/src/providers/integrations/cursor/cursor-http2.ts +2 -0
- package/src/providers/integrations/cursor/cursor-request.ts +35 -20
- package/src/providers/integrations/cursor/cursor-shared.ts +26 -5
- package/src/providers/integrations/cursor/cursor.ts +139 -70
- package/src/providers/integrations/cursor/protocol/messages.ts +22 -29
- package/src/providers/integrations/cursor/protocol/protobuf-schema.ts +37 -0
- 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 +558 -0
- package/src/providers/integrations/devin/protocol/framing.ts +124 -0
- package/src/providers/integrations/factory/factory-oauth.ts +17 -44
- package/src/providers/integrations/factory/factory.ts +2 -2
- package/src/providers/integrations/freebuff/assets/official-system-manager.txt +88 -0
- package/src/providers/integrations/freebuff/assets/official-system-worker.txt +57 -0
- package/src/providers/integrations/freebuff/assets/official-tools.json +1070 -0
- package/src/providers/integrations/freebuff/freebuff-catalog.ts +120 -0
- package/src/providers/integrations/freebuff/freebuff-constants.ts +1233 -0
- package/src/providers/integrations/freebuff/freebuff-discovery.ts +108 -0
- package/src/providers/integrations/freebuff/freebuff-oauth.ts +240 -0
- package/src/providers/integrations/freebuff/freebuff-quota.ts +260 -0
- package/src/providers/integrations/freebuff/freebuff-virtual.ts +89 -0
- package/src/providers/integrations/freebuff/freebuff.ts +682 -0
- 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/grok/grok-oauth.ts +48 -45
- package/src/providers/integrations/grok/grok-ratelimit-store.ts +10 -17
- package/src/providers/integrations/grok/grok-turn-index.ts +11 -16
- package/src/providers/integrations/hermes/hermes-oauth.ts +94 -0
- package/src/providers/integrations/{hermes.ts → hermes/hermes.ts} +13 -21
- package/src/providers/integrations/hermes/index.ts +2 -0
- package/src/providers/integrations/kiro/kiro-catalog.ts +17 -1
- package/src/providers/integrations/kiro/kiro-discovery.ts +10 -12
- package/src/providers/integrations/kiro/kiro-quota.ts +8 -4
- package/src/providers/integrations/kiro/kiro.ts +15 -19
- package/src/providers/integrations/ollamacloud/index.ts +2 -0
- package/src/providers/integrations/{ollamacloud-models.ts → ollamacloud/models.ts} +2 -2
- package/src/providers/integrations/{ollamacloud-quota.ts → ollamacloud/quota.ts} +3 -3
- package/src/providers/integrations/opencode/fingerprint.ts +1 -1
- package/src/providers/integrations/opencode/opencode-go-quota.ts +2 -8
- package/src/providers/integrations/perch/perch-catalog.ts +105 -0
- package/src/providers/integrations/perch/perch-credential.ts +25 -0
- package/src/providers/integrations/perch/perch-oauth.ts +170 -0
- package/src/providers/integrations/perch/perch-protocol.ts +208 -0
- package/src/providers/integrations/perch/perch-quota.ts +123 -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 +305 -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 +19 -60
- package/src/providers/integrations/qoder/qoder-adapter.ts +90 -40
- package/src/providers/integrations/qoder/qoder-auth.ts +101 -29
- package/src/providers/integrations/qoder/qoder-cache.ts +32 -0
- package/src/providers/integrations/qoder/qoder-catalog.ts +26 -6
- package/src/providers/integrations/qoder/qoder-chat.ts +7 -21
- package/src/providers/integrations/qoder/qoder-checkin.ts +6 -6
- package/src/providers/integrations/qoder/qoder-grpc.ts +210 -7
- package/src/providers/integrations/qoder/qoder-ide.ts +5 -108
- 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 -7
- 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 +12 -11
- package/src/providers/integrations/trae/trae-quota.ts +96 -36
- package/src/providers/integrations/trae/trae-realm.ts +74 -15
- package/src/providers/integrations/trae/trae.ts +99 -26
- package/src/providers/integrations/zcode-start-plan/provider.ts +226 -23
- package/src/providers/integrations/zcode-start-plan/quota.ts +19 -2
- package/src/providers/integrations/zro/zro.ts +204 -0
- package/src/providers/operations/account-error-classification.ts +235 -278
- package/src/providers/operations/account-health-service.ts +273 -26
- package/src/providers/operations/account-refresh-status.ts +269 -0
- package/src/providers/operations/account-tier.ts +66 -0
- package/src/providers/operations/client-version-resolver.ts +1 -1
- package/src/providers/operations/client-versions.ts +42 -150
- package/src/providers/operations/cooldown-rules.ts +56 -0
- package/src/providers/operations/{model-catalog-cache.ts → provider-caches.ts} +68 -16
- package/src/providers/operations/provider-catalog-seeder.ts +4 -0
- package/src/providers/operations/provider-catalog-service.ts +1 -1
- package/src/providers/operations/provider-credential-service.ts +102 -32
- package/src/providers/operations/provider-rules/github.ts +48 -0
- package/src/providers/operations/provider-rules/grok.ts +33 -0
- package/src/providers/operations/provider-rules/inception.ts +49 -0
- package/src/providers/operations/provider-rules/index.ts +32 -0
- package/src/providers/operations/provider-rules/perch.ts +37 -0
- package/src/providers/operations/provider-rules/qoder.ts +70 -0
- package/src/providers/operations/provider-rules/trae.ts +81 -0
- package/src/providers/provider-contracts.ts +33 -2
- package/src/providers/{provider-credential-resolver.ts → provider-credentials.ts} +40 -3
- package/src/providers/provider-metadata.ts +99 -14
- package/src/providers/provider-model-definition.ts +36 -0
- package/src/providers/provider-probe.ts +15 -1
- package/src/providers/provider-registry.ts +42 -33
- package/src/providers/quota/quota-limit-windows.ts +16 -5
- package/src/providers/quota/quota-support.ts +1 -1
- package/src/providers/quota/quota-window-parser.ts +90 -18
- package/src/providers/quota/refresh.ts +96 -18
- package/src/security/abuse.ts +71 -0
- package/src/security/api-key-auth-cache.ts +1 -1
- package/src/security/api-key-auth.ts +6 -2
- package/src/transport/dispatch/attempt-finalize.ts +14 -13
- package/src/transport/dispatch/attempt-loop.ts +71 -17
- package/src/transport/dispatch/generation.ts +2 -8
- package/src/transport/dispatch/native-shared.ts +25 -0
- package/src/transport/dispatch/proxy-request.ts +9 -46
- package/src/transport/dispatch/responses-compact.ts +2 -8
- package/src/transport/dispatch/streaming/finalize.ts +9 -6
- package/src/transport/dispatch/streaming/stream.ts +9 -36
- package/src/transport/dispatch/systemone.ts +2 -8
- package/src/transport/dispatch/token-count.ts +2 -6
- package/src/transport/dispatch/upstream.ts +67 -1
- package/src/transport/drain-endpoint.ts +31 -4
- package/src/transport/failure-policy.ts +129 -37
- package/src/transport/guards/body-policy.ts +7 -1
- package/src/transport/guards/error-lifecycle.ts +13 -1
- package/src/transport/guards/request-context.ts +22 -14
- package/src/transport/request/preparer.ts +81 -71
- package/src/transport/routing/bounded-lru.ts +14 -0
- package/src/transport/routing/drain-reserve.ts +129 -0
- package/src/transport/routing/plan-gate.ts +7 -1
- package/src/transport/routing/round-robin.ts +60 -0
- package/src/transport/routing/route-catalog.ts +123 -111
- package/src/transport/routing/route-model.ts +51 -9
- package/src/transport/routing/router.ts +418 -33
- package/src/transport/surface/adapters.ts +11 -2
- package/src/transport/surface/chat/adapter.ts +2 -2
- package/src/transport/surface/chat/encode.ts +47 -4
- package/src/transport/surface/chat/parse.ts +2 -12
- package/src/transport/surface/completion.ts +44 -33
- package/src/transport/surface/content-parts.ts +21 -0
- package/src/transport/surface/messages/adapter.ts +9 -2
- package/src/transport/surface/messages/encode.ts +11 -3
- package/src/transport/surface/messages/parse.ts +2 -1
- package/src/transport/surface/messages/stream.ts +33 -17
- package/src/transport/surface/responses/encode.ts +34 -6
- package/src/transport/surface/responses/parse.ts +3 -11
- 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 +0 -1
- 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.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/quota-DJ1QlAOq.js +0 -1
- package/dist/dashboard/assets/quota-DJ1QlAOq.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/bundled-discovery-providers.ts +0 -98
- package/src/providers/configured-openai-providers.ts +0 -120
- 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/src/providers/operations/model-discovery-cache.ts +0 -34
- package/src/providers/operations/probe-fetch.ts +0 -15
- package/src/providers/operations/provider-version-cache.ts +0 -30
- package/src/providers/provider-capabilities.ts +0 -33
- package/src/providers/provider-credential-envelope.ts +0 -43
- /package/dist/dashboard/assets/{app-CTxoqyYB.js → app-DIXLjojg.js} +0 -0
- /package/dist/dashboard/assets/{app-CTxoqyYB.js.gz → app-DIXLjojg.js.gz} +0 -0
|
@@ -47,7 +47,7 @@ import {
|
|
|
47
47
|
grok407ProbeFailure,
|
|
48
48
|
hasMeaningfulOutput,
|
|
49
49
|
loadProbePreferences,
|
|
50
|
-
|
|
50
|
+
PROBE_FAST_SAMPLE_MAX_CHARS,
|
|
51
51
|
recordProbeHealth,
|
|
52
52
|
resolveProbeAdapter,
|
|
53
53
|
resolveProbeTarget,
|
|
@@ -56,7 +56,7 @@ import {
|
|
|
56
56
|
wireContextFrom,
|
|
57
57
|
type ProbeProviderRow,
|
|
58
58
|
} from "./probe-phases";
|
|
59
|
-
import { createProbeFetch } from "../
|
|
59
|
+
import { createProbeFetch } from "../provider-probe";
|
|
60
60
|
import { authHeaders } from "../adapters/compatible-adapter";
|
|
61
61
|
import {
|
|
62
62
|
byokAuthHeaderShape,
|
|
@@ -188,14 +188,16 @@ export class ProviderProbingService {
|
|
|
188
188
|
request: ProbeModelRequest,
|
|
189
189
|
): Promise<ProbeModelResult> {
|
|
190
190
|
const startedAt = Date.now();
|
|
191
|
-
// Qoder's
|
|
192
|
-
// (
|
|
193
|
-
//
|
|
194
|
-
//
|
|
191
|
+
// Qoder's legacy COSY surface is the only live chat transport since
|
|
192
|
+
// 2026-11-07 (the gRPC host is NXDOMAIN and the IDE `/model/v1` surface
|
|
193
|
+
// answers 404 on every alive host). Probes for Qoder default to it unless
|
|
194
|
+
// the operator picked a surface explicitly. The IDE surface also echoes
|
|
195
|
+
// provider metadata, but that is worthless when the surface itself is
|
|
196
|
+
// dead — a probe must report what live traffic would do.
|
|
195
197
|
const normalizedRequest: ProbeModelRequest =
|
|
196
198
|
(providerId === "qoder" || providerId === "qodercn") &&
|
|
197
199
|
(request.surface === undefined || request.surface === "default")
|
|
198
|
-
? { ...request, surface: "
|
|
200
|
+
? { ...request, surface: "cosy" }
|
|
199
201
|
: request;
|
|
200
202
|
const modelId = normalizedRequest.modelId;
|
|
201
203
|
// One provider snapshot per probe: the contract gate, the account gate, the
|
|
@@ -241,24 +243,42 @@ export class ProviderProbingService {
|
|
|
241
243
|
};
|
|
242
244
|
}
|
|
243
245
|
|
|
244
|
-
const
|
|
246
|
+
const preferences = await loadProbePreferences({
|
|
245
247
|
db: this.db,
|
|
246
248
|
tenantId,
|
|
247
249
|
wireFamily,
|
|
248
250
|
request,
|
|
249
251
|
});
|
|
250
|
-
|
|
251
|
-
//
|
|
252
|
-
//
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
const
|
|
256
|
-
const probeRequest = qoderCmodelPrompt === undefined ? normalizedRequest : { ...normalizedRequest, prompt: qoderCmodelPrompt };
|
|
252
|
+
const { probeReasoning, payloadCapture } = preferences;
|
|
253
|
+
// `detail` is the exact pre-existing behaviour; only `fast` changes any
|
|
254
|
+
// budget. The identity prompt itself is shared by both modes (D42).
|
|
255
|
+
const fast = preferences.mode === "fast";
|
|
256
|
+
const probeTimeoutMs = preferences.timeoutMs ?? 30_000;
|
|
257
|
+
const probeSignal = AbortSignal.timeout(probeTimeoutMs);
|
|
257
258
|
const canonicalRequest = buildProbeCanonicalRequest({
|
|
258
259
|
modelId,
|
|
259
|
-
request:
|
|
260
|
+
request: normalizedRequest,
|
|
260
261
|
probeReasoning,
|
|
261
262
|
sourceSurface,
|
|
263
|
+
...(preferences.maxOutputTokens === undefined
|
|
264
|
+
? {}
|
|
265
|
+
: { maxOutputTokens: preferences.maxOutputTokens }),
|
|
266
|
+
// Freebuff's admission policy gate flags repeated identical probes as
|
|
267
|
+
// automated farming (`prompt_policy_refused` / "same message sent over
|
|
268
|
+
// and over"). Its probe must be minimal and always distinct per model —
|
|
269
|
+
// no worker system template, no shared identity question. `ping` is the
|
|
270
|
+
// same minimal greeting the fast-probe path already uses; the model id
|
|
271
|
+
// keeps every probe distinct so the "same message over and over" gate
|
|
272
|
+
// cannot flag a batch run.
|
|
273
|
+
...(providerId === "freebuff"
|
|
274
|
+
? {
|
|
275
|
+
defaultPromptOverride: `ping ${modelId}`,
|
|
276
|
+
providerSystemPrompt: undefined,
|
|
277
|
+
}
|
|
278
|
+
: {}),
|
|
279
|
+
...(typeof adapter?.systemPrompt === "function" && providerId !== "freebuff"
|
|
280
|
+
? { providerSystemPrompt: adapter.systemPrompt() }
|
|
281
|
+
: {}),
|
|
262
282
|
});
|
|
263
283
|
let capturedRequest = canonicalRequest;
|
|
264
284
|
const parsedProviderId = parseProviderId(providerId);
|
|
@@ -282,7 +302,6 @@ export class ProviderProbingService {
|
|
|
282
302
|
: { probe_surface: normalizedRequest.surface }),
|
|
283
303
|
};
|
|
284
304
|
|
|
285
|
-
const probeSignal = AbortSignal.timeout(30_000);
|
|
286
305
|
const events: CanonicalEvent[] = [];
|
|
287
306
|
/**
|
|
288
307
|
* Populated when the probe dials. Declared here, not inside the try, so the
|
|
@@ -337,7 +356,7 @@ export class ProviderProbingService {
|
|
|
337
356
|
candidate,
|
|
338
357
|
{
|
|
339
358
|
credential,
|
|
340
|
-
deadline: startedAt +
|
|
359
|
+
deadline: startedAt + probeTimeoutMs,
|
|
341
360
|
abort_signal: probeSignal,
|
|
342
361
|
outbound_fetch: probeFetch,
|
|
343
362
|
},
|
|
@@ -373,7 +392,7 @@ export class ProviderProbingService {
|
|
|
373
392
|
candidate,
|
|
374
393
|
{
|
|
375
394
|
credential,
|
|
376
|
-
deadline: startedAt +
|
|
395
|
+
deadline: startedAt + probeTimeoutMs,
|
|
377
396
|
abort_signal: probeSignal,
|
|
378
397
|
outbound_fetch: probeFetch,
|
|
379
398
|
},
|
|
@@ -415,7 +434,7 @@ export class ProviderProbingService {
|
|
|
415
434
|
capturedRequest = request;
|
|
416
435
|
for await (const event of adapter.dispatch(request, candidate, {
|
|
417
436
|
credential,
|
|
418
|
-
deadline: startedAt +
|
|
437
|
+
deadline: startedAt + probeTimeoutMs,
|
|
419
438
|
abort_signal: probeSignal,
|
|
420
439
|
outbound_fetch: probeFetch,
|
|
421
440
|
// A bidi-only provider (Cursor's Connect-RPC Run) has no `fetch`
|
|
@@ -433,7 +452,8 @@ export class ProviderProbingService {
|
|
|
433
452
|
if (!hasMeaningfulOutput(events)) throw new Error("probe response content empty");
|
|
434
453
|
} catch (error) {
|
|
435
454
|
const message = error instanceof Error ? error.message : String(error);
|
|
436
|
-
|
|
455
|
+
// `fast` is deliberately single-shot: no 1024-token streamed retry.
|
|
456
|
+
if (fast || !/empty\s+(?:response\s+)?content|response\s+content\s+empty/i.test(message)) {
|
|
437
457
|
throw error;
|
|
438
458
|
}
|
|
439
459
|
// Some reasoning models spend the short probe budget before producing
|
|
@@ -526,26 +546,40 @@ export class ProviderProbingService {
|
|
|
526
546
|
latencyMs,
|
|
527
547
|
});
|
|
528
548
|
|
|
529
|
-
// Probe telemetry rides the same batch buffer as live traffic.
|
|
530
|
-
//
|
|
531
|
-
//
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
+
// Probe telemetry rides the same batch buffer as live traffic. `fast`
|
|
550
|
+
// keeps only the liveness row — no token-speed/usage/TTFB detail — so a
|
|
551
|
+
// "test all models" run stays cheap; `detail` is unchanged.
|
|
552
|
+
if (fast) {
|
|
553
|
+
this.telemetryBuffer?.enqueue({
|
|
554
|
+
tenantId,
|
|
555
|
+
requestId,
|
|
556
|
+
sourceSurface,
|
|
557
|
+
requestedModel: modelId,
|
|
558
|
+
endpoint: endpointPath,
|
|
559
|
+
providerId,
|
|
560
|
+
latencyMs,
|
|
561
|
+
stream: false,
|
|
562
|
+
status: ok ? "completed" : "failed",
|
|
563
|
+
});
|
|
564
|
+
} else {
|
|
565
|
+
this.telemetryBuffer?.enqueue({
|
|
566
|
+
tenantId,
|
|
567
|
+
requestId,
|
|
568
|
+
sourceSurface,
|
|
569
|
+
requestedModel: modelId,
|
|
570
|
+
endpoint: endpointPath,
|
|
571
|
+
providerId,
|
|
572
|
+
...(accountId ? { accountId } : {}),
|
|
573
|
+
...(networkPoolId ? { networkPoolId } : {}),
|
|
574
|
+
latencyMs,
|
|
575
|
+
...(ttfbMs !== undefined ? { ttfbMs } : {}),
|
|
576
|
+
stream: capturedRequest.stream,
|
|
577
|
+
status: ok ? "completed" : "failed",
|
|
578
|
+
...(ok ? {} : { errorCategory: errorMessage ?? "probe_failed" }),
|
|
579
|
+
...(usage ? { usage } : {}),
|
|
580
|
+
...(tokensPerSec !== undefined ? { tokensPerSec } : {}),
|
|
581
|
+
});
|
|
582
|
+
}
|
|
549
583
|
if (this.telemetryBuffer && payloadCapture.enabled) {
|
|
550
584
|
if (payloadCapture.mode === "metadata") {
|
|
551
585
|
// Stage presence only: the provider request line (method, URL,
|
|
@@ -596,7 +630,13 @@ export class ProviderProbingService {
|
|
|
596
630
|
}
|
|
597
631
|
}
|
|
598
632
|
|
|
599
|
-
const
|
|
633
|
+
const rawSample = extractSample(events);
|
|
634
|
+
// `fast` answers one question — did the model talk back? — so an 80-char
|
|
635
|
+
// excerpt is enough; `detail` keeps the full sample verbatim (D40).
|
|
636
|
+
const sample =
|
|
637
|
+
rawSample === undefined || !fast
|
|
638
|
+
? rawSample
|
|
639
|
+
: rawSample.slice(0, PROBE_FAST_SAMPLE_MAX_CHARS);
|
|
600
640
|
// The HTTP-only `readReportedModel` covers response-body transports; a
|
|
601
641
|
// non-HTTP transport (Qoder gRPC) reports the upstream echo as a
|
|
602
642
|
// `response_start` event instead — fall back to that when the body read
|
|
@@ -611,11 +651,11 @@ export class ProviderProbingService {
|
|
|
611
651
|
...(accountId ? { accountId } : {}),
|
|
612
652
|
ok,
|
|
613
653
|
latencyMs,
|
|
614
|
-
...(ttfbMs
|
|
654
|
+
...(fast || ttfbMs === undefined ? {} : { ttfbMs }),
|
|
615
655
|
...(upstreamStatusCode === undefined ? {} : { statusCode: upstreamStatusCode }),
|
|
616
656
|
...(sample ? { sample } : {}),
|
|
617
|
-
...(reportedModel === undefined ? {} : { reportedModel }),
|
|
618
|
-
...(providerMetadata === undefined || Object.keys(providerMetadata).length === 0
|
|
657
|
+
...(fast || reportedModel === undefined ? {} : { reportedModel }),
|
|
658
|
+
...(fast || providerMetadata === undefined || Object.keys(providerMetadata).length === 0
|
|
619
659
|
? {}
|
|
620
660
|
: { providerMetadata }),
|
|
621
661
|
...(searchResults !== undefined && searchResults.length > 0 ? { searchResults } : {}),
|
|
@@ -717,38 +757,54 @@ export class ProviderProbingService {
|
|
|
717
757
|
}
|
|
718
758
|
}
|
|
719
759
|
|
|
720
|
-
|
|
760
|
+
/**
|
|
761
|
+
* Fetches one provider's model listing through the exact same two branches
|
|
762
|
+
* `syncModels` uses — a registry resolver, or the generic OpenAI-compatible
|
|
763
|
+
* `/models` fetch for BYOK — without writing anything. `supported:false`
|
|
764
|
+
* names a provider with no listing path so the Add-Model UI can offer its
|
|
765
|
+
* manual fallback instead of reporting a transport failure.
|
|
766
|
+
*
|
|
767
|
+
* The account is the first usable one (`selectUsableAccount`), the same
|
|
768
|
+
* selection sync uses (D27); its label is returned when available so the
|
|
769
|
+
* surface can say whose roster it is showing.
|
|
770
|
+
*/
|
|
771
|
+
async discoverModels(
|
|
772
|
+
tenantId: string,
|
|
773
|
+
providerId: string,
|
|
774
|
+
): Promise<{
|
|
775
|
+
readonly supported: boolean;
|
|
776
|
+
readonly models: readonly ModelDefinition[];
|
|
777
|
+
readonly accountLabel?: string;
|
|
778
|
+
/** Wire resolution context + BYOK family constraints for a later commit. */
|
|
779
|
+
readonly wire: {
|
|
780
|
+
readonly providerRow: ProbeProviderRow | undefined;
|
|
781
|
+
readonly supportedWireFamilies: readonly WireFamily[] | undefined;
|
|
782
|
+
readonly baseUrl: string | undefined;
|
|
783
|
+
};
|
|
784
|
+
}> {
|
|
721
785
|
const outbound = asOutboundBinding(await this.outboundFetchFor(tenantId, providerId));
|
|
722
786
|
try {
|
|
723
787
|
const discoveryFetch = asFetchLike(outbound.fetch);
|
|
724
788
|
const resolver = await this.providerRegistry.resolveModelDiscovery(providerId);
|
|
789
|
+
const account = await this.usableAccount(tenantId, providerId);
|
|
790
|
+
const providerRow = await this.loadProviderWireRow(providerId);
|
|
725
791
|
if (resolver) {
|
|
726
|
-
//
|
|
727
|
-
//
|
|
728
|
-
//
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
const account = await this.usableAccount(tenantId, providerId);
|
|
733
|
-
const secret = account?.secret;
|
|
734
|
-
const providerRow = await this.loadProviderWireRow(providerId);
|
|
735
|
-
const effectiveBaseUrl = providerRow?.baseUrl ?? providerBaseUrl(providerId) ?? "";
|
|
792
|
+
// `providerBaseUrl` throws for a non-bundled id; a custom provider can
|
|
793
|
+
// still register a discovery resolver, and its own row's base URL is
|
|
794
|
+
// the authority. Only a bundled provider falls back to metadata.
|
|
795
|
+
const effectiveBaseUrl =
|
|
796
|
+
providerRow?.baseUrl ??
|
|
797
|
+
(isBundledProviderId(providerId) ? providerBaseUrl(providerId) : "");
|
|
736
798
|
let discovered: readonly ModelDefinition[] | null;
|
|
737
799
|
try {
|
|
738
800
|
discovered = await resolver({
|
|
739
801
|
baseUrl: effectiveBaseUrl,
|
|
740
|
-
credential: secret ?? "",
|
|
802
|
+
credential: account?.secret ?? "",
|
|
741
803
|
...(discoveryFetch ? { fetcher: discoveryFetch } : {}),
|
|
742
804
|
...(account?.authState === undefined ? {} : { authState: account.authState }),
|
|
743
805
|
});
|
|
744
806
|
} catch (error) {
|
|
745
|
-
throw
|
|
746
|
-
"transport_unavailable",
|
|
747
|
-
502,
|
|
748
|
-
`Model discovery failed for ${providerId}: ${error instanceof Error ? error.message : "unknown error"} (request {requestId}). The connection ghosted us — come back in a moment 👻`,
|
|
749
|
-
{},
|
|
750
|
-
"upstream",
|
|
751
|
-
);
|
|
807
|
+
throw this.discoveryFailure(providerId, error);
|
|
752
808
|
}
|
|
753
809
|
if (!discovered) {
|
|
754
810
|
throw new GatewayError(
|
|
@@ -759,101 +815,92 @@ export class ProviderProbingService {
|
|
|
759
815
|
"upstream",
|
|
760
816
|
);
|
|
761
817
|
}
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
effectiveBaseUrl,
|
|
769
|
-
);
|
|
818
|
+
return {
|
|
819
|
+
supported: true,
|
|
820
|
+
models: discovered,
|
|
821
|
+
...(account?.label === undefined ? {} : { accountLabel: account.label }),
|
|
822
|
+
wire: { providerRow, supportedWireFamilies: undefined, baseUrl: effectiveBaseUrl },
|
|
823
|
+
};
|
|
770
824
|
}
|
|
771
|
-
|
|
772
|
-
const providerRow = await this.loadProviderWireRow(providerId);
|
|
773
825
|
if (providerRow?.baseUrl) {
|
|
774
|
-
const
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
}
|
|
808
|
-
if (discovered && discovered.length > 0) {
|
|
809
|
-
const ctx = wireContextFrom(providerRow);
|
|
810
|
-
return this.persistDiscoveredModels(
|
|
811
|
-
providerId,
|
|
812
|
-
discovered,
|
|
813
|
-
(modelId) => resolveDiscoveredWire(modelId, ctx, undefined, this.defaultEndpoints),
|
|
814
|
-
ctx.endpointPathsByWireFamily,
|
|
815
|
-
providerRow.baseUrl ?? undefined,
|
|
816
|
-
// The generic `/models` fetch guesses a wire from the model id;
|
|
817
|
-
// the row's own profile is what the adapter will enforce, so it
|
|
818
|
-
// decides which guess is admissible.
|
|
819
|
-
wireProfile.supportedWireFamilies,
|
|
820
|
-
);
|
|
821
|
-
}
|
|
826
|
+
const wireProfile = resolveByokWireProfile(
|
|
827
|
+
providerRow.wireFamilyDefault,
|
|
828
|
+
providerRow.compatibilityProfile as CompatibilityProfile | null,
|
|
829
|
+
);
|
|
830
|
+
const modelsBaseUrl = modelDiscoveryBaseUrl(providerRow.baseUrl);
|
|
831
|
+
let discovered: readonly ModelDefinition[] | null;
|
|
832
|
+
try {
|
|
833
|
+
discovered = await fetchOpenAICompatibleModels({
|
|
834
|
+
baseUrl: modelsBaseUrl,
|
|
835
|
+
providerId,
|
|
836
|
+
headers: {
|
|
837
|
+
...authHeaders(
|
|
838
|
+
wireProfile.authHeaderShape,
|
|
839
|
+
new TextEncoder().encode(account?.secret ?? ""),
|
|
840
|
+
"api_key",
|
|
841
|
+
),
|
|
842
|
+
},
|
|
843
|
+
...(discoveryFetch ? { fetcher: discoveryFetch as typeof fetch } : {}),
|
|
844
|
+
});
|
|
845
|
+
} catch (error) {
|
|
846
|
+
throw this.discoveryFailure(providerId, error);
|
|
847
|
+
}
|
|
848
|
+
if (discovered && discovered.length > 0) {
|
|
849
|
+
return {
|
|
850
|
+
supported: true,
|
|
851
|
+
models: discovered,
|
|
852
|
+
...(account?.label === undefined ? {} : { accountLabel: account.label }),
|
|
853
|
+
wire: {
|
|
854
|
+
providerRow,
|
|
855
|
+
supportedWireFamilies: wireProfile.supportedWireFamilies,
|
|
856
|
+
baseUrl: providerRow.baseUrl ?? undefined,
|
|
857
|
+
},
|
|
858
|
+
};
|
|
822
859
|
}
|
|
823
860
|
}
|
|
861
|
+
return {
|
|
862
|
+
supported: false,
|
|
863
|
+
models: [],
|
|
864
|
+
...(account?.label === undefined ? {} : { accountLabel: account.label }),
|
|
865
|
+
wire: { providerRow, supportedWireFamilies: undefined, baseUrl: undefined },
|
|
866
|
+
};
|
|
867
|
+
} finally {
|
|
868
|
+
outbound.release?.();
|
|
869
|
+
}
|
|
870
|
+
}
|
|
824
871
|
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
872
|
+
/** One upstream-shaped error for both discovery branches. */
|
|
873
|
+
private discoveryFailure(providerId: string, error: unknown): GatewayError {
|
|
874
|
+
return new GatewayError(
|
|
875
|
+
"transport_unavailable",
|
|
876
|
+
502,
|
|
877
|
+
`Model discovery failed for ${providerId}: ${error instanceof Error ? error.message : "unknown error"} (request {requestId}). The connection ghosted us — come back in a moment 👻`,
|
|
878
|
+
{},
|
|
879
|
+
"upstream",
|
|
880
|
+
);
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
/**
|
|
884
|
+
* The single write path for every discovery result: one fresh fetch, one
|
|
885
|
+
* subset, one source. Sync passes the whole roster as `discovered`; the
|
|
886
|
+
* Add-Model commit passes the operator's selected definitions as `manual`.
|
|
887
|
+
* `definitionsFrom` is applied to the fresh fetch so a stale client-side
|
|
888
|
+
* `ModelDefinition` can never decide what gets stored.
|
|
889
|
+
*/
|
|
890
|
+
private async persistRoster(
|
|
891
|
+
tenantId: string,
|
|
892
|
+
providerId: string,
|
|
893
|
+
definitionsFrom: (
|
|
894
|
+
fresh: readonly ModelDefinition[],
|
|
895
|
+
) => readonly ModelDefinition[],
|
|
896
|
+
source?: "manual",
|
|
897
|
+
unsupportedError?: (accountExists: boolean) => GatewayError,
|
|
898
|
+
): Promise<{ synced: number }> {
|
|
899
|
+
const fresh = await this.discoverModels(tenantId, providerId);
|
|
900
|
+
if (!fresh.supported) {
|
|
901
|
+
if (unsupportedError) {
|
|
902
|
+
throw unsupportedError((await this.usableAccount(tenantId, providerId)) !== undefined);
|
|
854
903
|
}
|
|
855
|
-
// A credential exists and the provider still has no listing path: the
|
|
856
|
-
// catalog is the compiled one, and new models arrive with an update.
|
|
857
904
|
throw new GatewayError(
|
|
858
905
|
"capability_unsupported",
|
|
859
906
|
400,
|
|
@@ -861,9 +908,75 @@ export class ProviderProbingService {
|
|
|
861
908
|
{},
|
|
862
909
|
"cartethyia",
|
|
863
910
|
);
|
|
864
|
-
} finally {
|
|
865
|
-
outbound.release?.();
|
|
866
911
|
}
|
|
912
|
+
const ctx = wireContextFrom(fresh.wire.providerRow);
|
|
913
|
+
return this.persistDiscoveredModels(
|
|
914
|
+
providerId,
|
|
915
|
+
definitionsFrom(fresh.models),
|
|
916
|
+
(modelId) => resolveDiscoveredWire(modelId, ctx, undefined, this.defaultEndpoints),
|
|
917
|
+
ctx.endpointPathsByWireFamily,
|
|
918
|
+
fresh.wire.baseUrl,
|
|
919
|
+
fresh.wire.supportedWireFamilies,
|
|
920
|
+
source,
|
|
921
|
+
);
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
/**
|
|
925
|
+
* Full sync: one fetch, upsert every id as `discovered` (or `auto_free`).
|
|
926
|
+
* Its original prerequisite diagnostics are preserved: a custom provider
|
|
927
|
+
* with no account is told to add one, a bundled provider with no listing
|
|
928
|
+
* endpoint is told its catalog ships with the release.
|
|
929
|
+
*/
|
|
930
|
+
async syncModels(tenantId: string, providerId: string): Promise<{ synced: number }> {
|
|
931
|
+
return this.persistRoster(
|
|
932
|
+
tenantId,
|
|
933
|
+
providerId,
|
|
934
|
+
(fresh) => fresh,
|
|
935
|
+
undefined,
|
|
936
|
+
(accountExists) => {
|
|
937
|
+
if (!isBundledProviderId(providerId) || !accountExists) {
|
|
938
|
+
const prefix = isBundledProviderId(providerId)
|
|
939
|
+
? `Add an account for ${providerId} before fetching its models`
|
|
940
|
+
: `Add an account for ${providerId} first — model listing needs a credential`;
|
|
941
|
+
return new GatewayError(
|
|
942
|
+
"invalid_request",
|
|
943
|
+
400,
|
|
944
|
+
`${prefix} (request {requestId}). Wrong key, right door — check the credential 🔑`,
|
|
945
|
+
{},
|
|
946
|
+
"cartethyia",
|
|
947
|
+
);
|
|
948
|
+
}
|
|
949
|
+
return new GatewayError(
|
|
950
|
+
"capability_unsupported",
|
|
951
|
+
400,
|
|
952
|
+
`${providerId} publishes no model-listing endpoint (request {requestId}). Its catalog ships with Cartethyia; new models appear with an update. Know your tools — this one just isn't in the box 🧰`,
|
|
953
|
+
{},
|
|
954
|
+
"cartethyia",
|
|
955
|
+
);
|
|
956
|
+
},
|
|
957
|
+
);
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
/**
|
|
961
|
+
* Persists an operator-selected subset of a freshly fetched roster as
|
|
962
|
+
* `source="manual"` (D28). The store has already intersected the requested
|
|
963
|
+
* ids against its own fetch; this second fetch is the anti-forgery boundary
|
|
964
|
+
* and the source of every stored `ModelDefinition`.
|
|
965
|
+
*/
|
|
966
|
+
async commitDiscoveredModels(
|
|
967
|
+
tenantId: string,
|
|
968
|
+
providerId: string,
|
|
969
|
+
selectedIds: readonly string[],
|
|
970
|
+
): Promise<{ synced: number }> {
|
|
971
|
+
return this.persistRoster(
|
|
972
|
+
tenantId,
|
|
973
|
+
providerId,
|
|
974
|
+
(fresh) => {
|
|
975
|
+
const wanted = new Set(selectedIds);
|
|
976
|
+
return fresh.filter((definition) => wanted.has(definition.modelId));
|
|
977
|
+
},
|
|
978
|
+
"manual",
|
|
979
|
+
);
|
|
867
980
|
}
|
|
868
981
|
|
|
869
982
|
private async persistDiscoveredModels(
|
|
@@ -873,6 +986,7 @@ export class ProviderProbingService {
|
|
|
873
986
|
endpointOverrides?: Partial<Record<WireFamily, string>>,
|
|
874
987
|
baseUrl?: string,
|
|
875
988
|
supportedWireFamilies?: readonly WireFamily[],
|
|
989
|
+
source?: "manual",
|
|
876
990
|
): Promise<{ synced: number }> {
|
|
877
991
|
// Static definitions are the injected bundled catalog; the probing service
|
|
878
992
|
// and the console store are both handed the same startup-built map.
|
|
@@ -892,6 +1006,10 @@ export class ProviderProbingService {
|
|
|
892
1006
|
// rows for the same ids afterwards.
|
|
893
1007
|
const resolvedPairs: Array<{ modelId: string; endpointPath: string }> = [];
|
|
894
1008
|
const rows: Array<typeof models.$inferInsert> = [];
|
|
1009
|
+
// A `manual` commit is an operator's subset, not a full roster. The
|
|
1010
|
+
// builtin-id skip that protects sync from creating shadow rows would hide
|
|
1011
|
+
// a legitimately selected operator row, so only sync applies it.
|
|
1012
|
+
const skipBuiltinIds = source === undefined;
|
|
895
1013
|
// `(modelId, endpointPath)` is the row identity and the upsert's conflict
|
|
896
1014
|
// target, so a listing that repeats a pair (the bespoke discoveries return
|
|
897
1015
|
// raw upstream ids) would make a multi-row `ON CONFLICT DO UPDATE` fail with
|
|
@@ -900,7 +1018,7 @@ export class ProviderProbingService {
|
|
|
900
1018
|
const seenPairs = new Set<string>();
|
|
901
1019
|
for (const item of discoveredModels) {
|
|
902
1020
|
const modelId = typeof item === "string" ? item : item.modelId;
|
|
903
|
-
if (builtinIds.has(modelId)) continue;
|
|
1021
|
+
if (skipBuiltinIds && builtinIds.has(modelId)) continue;
|
|
904
1022
|
const resolved = resolve(modelId);
|
|
905
1023
|
const discDef = typeof item === "string" ? undefined : item;
|
|
906
1024
|
const { wireFamily, endpointPath } = applyDiscoveredWire({
|
|
@@ -927,6 +1045,10 @@ export class ProviderProbingService {
|
|
|
927
1045
|
reasoning: knownDefinition?.reasoning ?? discDef?.reasoning ?? false,
|
|
928
1046
|
toolCall: knownDefinition?.toolCall ?? discDef?.toolCall ?? false,
|
|
929
1047
|
cost: modelsDevCatalog.costFor(providerId, modelId),
|
|
1048
|
+
// R-E: fetched warnings win, a matching static definition is the
|
|
1049
|
+
// fallback, and an unknown id stores `NULL` (operator-editable).
|
|
1050
|
+
warningMetadata:
|
|
1051
|
+
discDef?.warnings ?? knownDefinition?.warnings ?? null,
|
|
930
1052
|
// Discovery builds its rows from the same static catalog, so a fetched
|
|
931
1053
|
// row carries the same answer a seeded one does. Null when neither
|
|
932
1054
|
// source marked it, which reads as "unknown" rather than "free".
|
|
@@ -948,7 +1070,8 @@ export class ProviderProbingService {
|
|
|
948
1070
|
// source, because the model list groups on it: a free-tier row is not
|
|
949
1071
|
// an ordinary fetched one, and collapsing both into `discovered` lost
|
|
950
1072
|
// that distinction at the only point where it is still known.
|
|
951
|
-
source:
|
|
1073
|
+
source:
|
|
1074
|
+
source ?? (discDef?.freeTier === true ? "auto_free" : "discovered"),
|
|
952
1075
|
sourceUpdatedAt,
|
|
953
1076
|
enabled: true,
|
|
954
1077
|
...(discDef?.dialect === undefined && knownDefinition?.dialect === undefined
|
|
@@ -975,6 +1098,7 @@ export class ProviderProbingService {
|
|
|
975
1098
|
cost: sql`excluded.cost`,
|
|
976
1099
|
proOnly: sql`excluded.pro_only`,
|
|
977
1100
|
freeTier: sql`excluded.free_tier`,
|
|
1101
|
+
warningMetadata: sql`excluded.warning_metadata`,
|
|
978
1102
|
dialect: sql`excluded.dialect`,
|
|
979
1103
|
// The proposed row's own source, not a constant: a free-tier
|
|
980
1104
|
// discovery and an ordinary one write the same upsert, and pinning
|
|
@@ -986,6 +1110,9 @@ export class ProviderProbingService {
|
|
|
986
1110
|
});
|
|
987
1111
|
}
|
|
988
1112
|
const synced = rows.length;
|
|
1113
|
+
// A manual commit is an operator's selection, not a roster replacement:
|
|
1114
|
+
// pruning would delete unrelated discovered rows with the same ids.
|
|
1115
|
+
if (source === "manual") return { synced };
|
|
989
1116
|
// Drop this provider's discovered rows for the synced ids that are no
|
|
990
1117
|
// longer part of the resolved set — the stale `(model, endpoint)` pair left
|
|
991
1118
|
// behind when a corrected wire family moved a model to a new path. Rows the
|
|
@@ -1015,13 +1142,6 @@ export class ProviderProbingService {
|
|
|
1015
1142
|
return { synced };
|
|
1016
1143
|
}
|
|
1017
1144
|
|
|
1018
|
-
private async usableAccountSecret(
|
|
1019
|
-
tenantId: string,
|
|
1020
|
-
providerId: string,
|
|
1021
|
-
): Promise<string | undefined> {
|
|
1022
|
-
return (await this.usableAccount(tenantId, providerId))?.secret;
|
|
1023
|
-
}
|
|
1024
|
-
|
|
1025
1145
|
/**
|
|
1026
1146
|
* The account a credential-scoped discovery will use, with its non-secret
|
|
1027
1147
|
* auth configuration. Selection is unchanged — first usable row, preferring a
|
|
@@ -1030,7 +1150,11 @@ export class ProviderProbingService {
|
|
|
1030
1150
|
private async usableAccount(
|
|
1031
1151
|
tenantId: string,
|
|
1032
1152
|
providerId: string,
|
|
1033
|
-
): Promise<{
|
|
1153
|
+
): Promise<{
|
|
1154
|
+
readonly secret: string;
|
|
1155
|
+
readonly label?: string;
|
|
1156
|
+
readonly authState?: Readonly<Record<string, unknown>>;
|
|
1157
|
+
} | undefined> {
|
|
1034
1158
|
const rows = await this.db
|
|
1035
1159
|
.select()
|
|
1036
1160
|
.from(providerAccounts)
|
|
@@ -1045,6 +1169,7 @@ export class ProviderProbingService {
|
|
|
1045
1169
|
const authState = isRecord(usable.authState) ? usable.authState as Readonly<Record<string, unknown>> : undefined;
|
|
1046
1170
|
return {
|
|
1047
1171
|
secret: decryptCredentialToString(usable.credentialCiphertext),
|
|
1172
|
+
...(typeof usable.label === "string" ? { label: usable.label } : {}),
|
|
1048
1173
|
...(authState === undefined ? {} : { authState }),
|
|
1049
1174
|
};
|
|
1050
1175
|
}
|
|
@@ -1056,6 +1181,9 @@ export class ProviderProbingService {
|
|
|
1056
1181
|
* carries its model route and service kind so native services never fall
|
|
1057
1182
|
* through to an LLM probe.
|
|
1058
1183
|
*
|
|
1184
|
+
* `mode` flows through every request, so "Test all models" (D43) can use
|
|
1185
|
+
* the cheap `fast` path end to end.
|
|
1186
|
+
*
|
|
1059
1187
|
* Sliding window beats barrel-batching here: the historical pattern waited
|
|
1060
1188
|
* for the slowest probe in each group of `N` before starting the next group,
|
|
1061
1189
|
* so one slow route stalled nine others. With the worker loop, a worker
|