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
|
@@ -16,10 +16,14 @@
|
|
|
16
16
|
*/
|
|
17
17
|
import type { CartethyiaDatabase } from "../../../persistence/db-handle";
|
|
18
18
|
import type { ValidatedFetch } from "../../../network/outbound-fetch";
|
|
19
|
-
import { getQoderVersion } from "../../operations/client-versions";
|
|
20
19
|
import { qoderRealmForProvider, QODER_REALMS } from "./qoder-realm";
|
|
20
|
+
import { exchangeQoderPatForHost } from "./qoder-auth";
|
|
21
21
|
import {
|
|
22
|
+
asNumber,
|
|
23
|
+
asRecord,
|
|
22
24
|
buildCampaignHeaders,
|
|
25
|
+
normalizeCampaign,
|
|
26
|
+
safeParse,
|
|
23
27
|
} from "./qoder-checkin";
|
|
24
28
|
import { qoderMachineHeaders } from "./qoder-umid";
|
|
25
29
|
import {
|
|
@@ -38,31 +42,6 @@ export const QODER_CREDIT_PROVIDER_IDS = ["qoder", "qodercn"] as const;
|
|
|
38
42
|
|
|
39
43
|
const PAT_PREFIX = "pt-";
|
|
40
44
|
|
|
41
|
-
function asRecord(value: unknown): Record<string, unknown> | null {
|
|
42
|
-
if (typeof value !== "object" || value === null) return null;
|
|
43
|
-
return value as Record<string, unknown>;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function asNumber(value: unknown): number | null {
|
|
47
|
-
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
48
|
-
if (typeof value === "string" && value.trim() !== "") {
|
|
49
|
-
const parsed = Number(value);
|
|
50
|
-
return Number.isFinite(parsed) ? parsed : null;
|
|
51
|
-
}
|
|
52
|
-
return null;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function normalizeCampaign(raw: Record<string, unknown>): Record<string, unknown> {
|
|
56
|
-
const benefit = asRecord(raw["benefit"]);
|
|
57
|
-
return {
|
|
58
|
-
campaign_id: String(raw["campaignId"] ?? raw["campaign_id"] ?? ""),
|
|
59
|
-
campaign_key: String(raw["campaignKey"] ?? raw["campaign_key"] ?? ""),
|
|
60
|
-
action_type: String(raw["actionType"] ?? raw["action_type"] ?? ""),
|
|
61
|
-
claim_status: String(raw["claimStatus"] ?? raw["claim_status"] ?? ""),
|
|
62
|
-
...(benefit !== null ? { benefit } : {}),
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
|
|
66
45
|
/**
|
|
67
46
|
* Exchanges a PAT (`pt-…`) for a job token (`jt-…`) on the realm's openapi
|
|
68
47
|
* host. Returns the job token, or `null` when the exchange fails.
|
|
@@ -70,7 +49,8 @@ function normalizeCampaign(raw: Record<string, unknown>): Record<string, unknown
|
|
|
70
49
|
* The real client binds the machine identity at exchange time
|
|
71
50
|
* (`getMachineIdentityRequestFields`), so `machine_id` + `machine_token` are
|
|
72
51
|
* sent when available — the job token is then bound to that machine. The
|
|
73
|
-
*
|
|
52
|
+
* request is the shared `exchangeQoderPatForHost` (same v1 body and client
|
|
53
|
+
* identity headers as the auth path), parameterized by this site's own host.
|
|
74
54
|
*/
|
|
75
55
|
async function exchangePatToJobToken(
|
|
76
56
|
base: string,
|
|
@@ -78,30 +58,17 @@ async function exchangePatToJobToken(
|
|
|
78
58
|
fetcher: ValidatedFetch,
|
|
79
59
|
machine?: { readonly id?: string; readonly token?: string },
|
|
80
60
|
): Promise<string | null> {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
"Cosy-Version": version,
|
|
93
|
-
},
|
|
94
|
-
body: JSON.stringify(body),
|
|
95
|
-
signal: AbortSignal.timeout(25_000),
|
|
96
|
-
});
|
|
97
|
-
if (!res.ok) return null;
|
|
98
|
-
const payload: unknown = await res.json().catch(() => null);
|
|
99
|
-
const rec = asRecord(payload);
|
|
100
|
-
const token = String(rec?.["token"] ?? rec?.["device_token"] ?? rec?.["access_token"] ?? "");
|
|
101
|
-
return token.length > 0 ? token : null;
|
|
102
|
-
} catch {
|
|
103
|
-
return null;
|
|
104
|
-
}
|
|
61
|
+
const exchanged = await exchangeQoderPatForHost({
|
|
62
|
+
pat,
|
|
63
|
+
url: `${base}/api/v1/jobToken/exchange`,
|
|
64
|
+
...(machine === undefined ? {} : { machine }),
|
|
65
|
+
fetcher,
|
|
66
|
+
signal: AbortSignal.timeout(25_000),
|
|
67
|
+
tokenKeys: ["token", "device_token", "access_token"],
|
|
68
|
+
});
|
|
69
|
+
if (exchanged === null) return null;
|
|
70
|
+
if (!exchanged.response.ok) return null;
|
|
71
|
+
return exchanged.result?.token ?? null;
|
|
105
72
|
}
|
|
106
73
|
|
|
107
74
|
/**
|
|
@@ -139,7 +106,7 @@ export async function claimQoderCredits(args: {
|
|
|
139
106
|
const store = new DrizzleQoderNativeIdentityStore(db);
|
|
140
107
|
let bound = await store.get(providerId, accountId);
|
|
141
108
|
if (!bound) {
|
|
142
|
-
const minted = await allocateQoderNativeIdentity(store, providerId, accountId);
|
|
109
|
+
const minted = await allocateQoderNativeIdentity(store, providerId, accountId, { realm });
|
|
143
110
|
if (!minted) return { state: "error", amount: 0, detail: "Could not mint a UMID identity for this account." };
|
|
144
111
|
bound = minted;
|
|
145
112
|
}
|
|
@@ -248,11 +215,3 @@ export async function claimQoderCredits(args: {
|
|
|
248
215
|
return { state: "error", amount: 0, detail: "Claim request failed." };
|
|
249
216
|
}
|
|
250
217
|
}
|
|
251
|
-
|
|
252
|
-
function safeParse(raw: string): unknown {
|
|
253
|
-
try {
|
|
254
|
-
return JSON.parse(raw);
|
|
255
|
-
} catch {
|
|
256
|
-
return null;
|
|
257
|
-
}
|
|
258
|
-
}
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Qoder adapter — transport selection, credential resolution, dispatch.
|
|
3
3
|
*
|
|
4
|
-
* Surface contract (2026-11-
|
|
5
|
-
* - `
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* scheme (AES/RSA + alphabet-scrambled body).
|
|
4
|
+
* Surface contract (2026-11-07):
|
|
5
|
+
* - `cosy` (default, production): `/algo/.../agent_chat_generation` on
|
|
6
|
+
* `api1` → `api2` → `api3.qoder.sh` (legacy COSY) — the only surface
|
|
7
|
+
* verified alive. Accepts free-tier rows (`lite`).
|
|
8
|
+
* - `grpc` (`model.chat.ChatService/ChatCompletionStream`): **not the
|
|
9
|
+
* default anymore** — its host (`api2-v2.qoder.sh`) is NXDOMAIN since
|
|
10
|
+
* 2026-11-07. Kept as an explicit override for probes.
|
|
12
11
|
* - `ide` (`/model/v1/chat/completions`): **probe/testing only — never
|
|
13
12
|
* production.** It is the only surface that echoes strategy metadata
|
|
14
13
|
* (`raw_usage` → provider_model/target) and it streams immediately, but its
|
|
@@ -31,12 +30,13 @@ import { abortGatewayError, createUpstreamDeadlineLifecycle } from "../../operat
|
|
|
31
30
|
import { getQoderVersion } from "../../operations/client-versions";
|
|
32
31
|
import { qoderMachineHostname, qoderMachineOs } from "../../operations/cli-platform";
|
|
33
32
|
import { mapUpstreamHttpError } from "../../../transport/failure-policy";
|
|
34
|
-
import { QODER_CN_MODEL_CONFIGS, QODER_INTL_MODEL_CONFIGS
|
|
33
|
+
import { QODER_CN_MODEL_CONFIGS, QODER_INTL_MODEL_CONFIGS } from "./qoder-catalog";
|
|
35
34
|
import { isQoderVirtualModel, qoderDispatchModelId } from "./qoder-virtual";
|
|
36
35
|
import { isQoderPat, qoderOAuthAuth, qoderSessionId, resolveQoderAuth } from "./qoder-auth";
|
|
37
36
|
import { buildQoderRequest, callQoder } from "./qoder-chat";
|
|
38
37
|
import { buildQoderGrpcRequest, qoderGrpcMetadata, qoderGrpcToCanonicalEvents } from "./qoder-grpc";
|
|
39
38
|
import { buildQoderIdeRequest, qoderBodyToCanonicalEvents } from "./qoder-ide";
|
|
39
|
+
import { estimateQoderSessionCache } from "./qoder-cache";
|
|
40
40
|
import {
|
|
41
41
|
QODER_CHAT_PATH,
|
|
42
42
|
QODER_PROVIDER_ID,
|
|
@@ -55,17 +55,16 @@ interface QoderAdapterOptions {
|
|
|
55
55
|
/**
|
|
56
56
|
* Which chat transport to dispatch on.
|
|
57
57
|
*
|
|
58
|
-
* `
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
* (`quota exceeded` 116) and must use the legacy COSY surface.
|
|
58
|
+
* `cosy` (default) = legacy COSY (`/algo/.../agent_chat_generation`) on
|
|
59
|
+
* `api1` → `api2` → `api3.qoder.sh` — the only surface verified alive
|
|
60
|
+
* (2026-11-07; the `api2-v2` model-server host is NXDOMAIN). This path also
|
|
61
|
+
* accepts free-tier rows (`lite`).
|
|
63
62
|
*
|
|
64
|
-
* `
|
|
65
|
-
* — the
|
|
63
|
+
* `grpc` forces the gRPC surface (`model.chat.ChatService/ChatCompletionStream`)
|
|
64
|
+
* — **no longer the default**; its host is not verified alive.
|
|
66
65
|
*
|
|
67
|
-
* `ide` forces
|
|
68
|
-
*
|
|
66
|
+
* `ide` forces `/model/v1` — **probe/testing only, never production**
|
|
67
|
+
* (see module doc).
|
|
69
68
|
*/
|
|
70
69
|
readonly surface?: "grpc" | "cosy" | "ide";
|
|
71
70
|
}
|
|
@@ -75,7 +74,7 @@ export function resolveQoderTransport(
|
|
|
75
74
|
surface: "grpc" | "cosy" | "ide" | undefined,
|
|
76
75
|
modelConfig: QoderModelConfig,
|
|
77
76
|
): "grpc" | "cosy" | "ide" {
|
|
78
|
-
const chosen = surface ?? "
|
|
77
|
+
const chosen = surface ?? "cosy";
|
|
79
78
|
// `ide` is an explicit testing override — never auto-selected, and it does
|
|
80
79
|
// not apply the free-tier rule (a forced IDE call is a test call).
|
|
81
80
|
if (chosen === "ide") return "ide";
|
|
@@ -85,16 +84,6 @@ export function resolveQoderTransport(
|
|
|
85
84
|
return chosen;
|
|
86
85
|
}
|
|
87
86
|
|
|
88
|
-
/**
|
|
89
|
-
* The widest selectable context window for one model key (from the live list's
|
|
90
|
-
* `context_config`; falls back to the static roster's stated window).
|
|
91
|
-
*/
|
|
92
|
-
function qoderMaxContextFor(modelId: string, modelConfig: QoderModelConfig, realm: QoderRealm): number {
|
|
93
|
-
const discovered = qoderDiscoveredMaxContexts(realm).get(modelId);
|
|
94
|
-
if (discovered !== undefined) return discovered;
|
|
95
|
-
return modelConfig.max_input_tokens;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
87
|
class QoderAdapter implements ProviderAdapter {
|
|
99
88
|
readonly provider_id: ProviderId;
|
|
100
89
|
private readonly realm: QoderRealm;
|
|
@@ -104,7 +93,14 @@ class QoderAdapter implements ProviderAdapter {
|
|
|
104
93
|
constructor(options: QoderAdapterOptions = {}) {
|
|
105
94
|
this.provider_id = (options.providerId ?? QODER_PROVIDER_ID) as ProviderId;
|
|
106
95
|
this.realm = options.realm ?? "intl";
|
|
107
|
-
|
|
96
|
+
// The legacy COSY surface is the production default (2026-11-07): the
|
|
97
|
+
// gRPC host is NXDOMAIN and the IDE surface is probe-only. The constructor
|
|
98
|
+
// must default to it too, or every billed model dispatches to the dead
|
|
99
|
+
// gRPC path while free-tier rows get cosy via the is_free rule — the
|
|
100
|
+
// mismatch that made `deepseek-v4.1-flash` fail while `qwen3.8-flash`
|
|
101
|
+
// passed. This is the same default `resolveQoderTransport(undefined)`
|
|
102
|
+
// already returns.
|
|
103
|
+
this.surface = options.surface ?? "cosy";
|
|
108
104
|
this.fetchFn = options.fetch ?? globalThis.fetch;
|
|
109
105
|
}
|
|
110
106
|
|
|
@@ -121,6 +117,30 @@ class QoderAdapter implements ProviderAdapter {
|
|
|
121
117
|
>;
|
|
122
118
|
}
|
|
123
119
|
|
|
120
|
+
/**
|
|
121
|
+
* Wraps an upstream event stream and applies the local session-cache
|
|
122
|
+
* estimate to every `usage` record (and terminal-carried usage).
|
|
123
|
+
*
|
|
124
|
+
* Qoder's upstream never reports prompt-cache hits (verified live on both
|
|
125
|
+
* gRPC and IDE HTTP surfaces), so the gap is closed the same way Trae and
|
|
126
|
+
* Kiro do: estimate the cached prefix from this session's request history.
|
|
127
|
+
*/
|
|
128
|
+
async *#withSessionCacheEstimate(
|
|
129
|
+
events: AsyncIterable<CanonicalEvent>,
|
|
130
|
+
request: CanonicalRequest,
|
|
131
|
+
sessionId: string | undefined,
|
|
132
|
+
): AsyncGenerator<CanonicalEvent> {
|
|
133
|
+
for await (const event of events) {
|
|
134
|
+
if (event.type === "usage") {
|
|
135
|
+
yield { ...event, usage: estimateQoderSessionCache(event.usage, sessionId, request.messages) };
|
|
136
|
+
} else if (event.type === "terminal" && event.usage !== undefined) {
|
|
137
|
+
yield { ...event, usage: estimateQoderSessionCache(event.usage, sessionId, request.messages) };
|
|
138
|
+
} else {
|
|
139
|
+
yield event;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
124
144
|
async *dispatch(
|
|
125
145
|
request: CanonicalRequest,
|
|
126
146
|
candidate: ProviderDispatchTarget,
|
|
@@ -207,7 +227,11 @@ class QoderAdapter implements ProviderAdapter {
|
|
|
207
227
|
if (!res.ok) throw await mapUpstreamHttpError(res, "qoder");
|
|
208
228
|
if (!res.body) throw new GatewayError("platform_unavailable", 502, "Qoder returned empty body (request {requestId}). The upstream came back empty-handed — retry 🌙", {}, "upstream");
|
|
209
229
|
lifecycle.release();
|
|
210
|
-
yield*
|
|
230
|
+
yield* this.#withSessionCacheEstimate(
|
|
231
|
+
qoderBodyToCanonicalEvents(res.body as ReadableStream<Uint8Array>, lifecycle.signal, modelId),
|
|
232
|
+
request,
|
|
233
|
+
qoderSessionId(auth.userId, modelId),
|
|
234
|
+
);
|
|
211
235
|
return;
|
|
212
236
|
}
|
|
213
237
|
|
|
@@ -222,7 +246,6 @@ class QoderAdapter implements ProviderAdapter {
|
|
|
222
246
|
modelId,
|
|
223
247
|
request,
|
|
224
248
|
auth.userId,
|
|
225
|
-
qoderMaxContextFor(modelId, modelConfig, this.realm),
|
|
226
249
|
{
|
|
227
250
|
messages: legacy.messages as Array<Record<string, unknown>>,
|
|
228
251
|
tools: legacy.tools as Array<Record<string, unknown>>,
|
|
@@ -233,16 +256,39 @@ class QoderAdapter implements ProviderAdapter {
|
|
|
233
256
|
);
|
|
234
257
|
const grpcMetadata = qoderGrpcMetadata(auth);
|
|
235
258
|
lifecycle.release();
|
|
236
|
-
yield*
|
|
259
|
+
yield* this.#withSessionCacheEstimate(
|
|
260
|
+
qoderGrpcToCanonicalEvents(grpcRequest, grpcMetadata, this.realm, lifecycle.signal, context.provider_capture),
|
|
261
|
+
request,
|
|
262
|
+
qoderSessionId(auth.userId, modelId),
|
|
263
|
+
);
|
|
237
264
|
return;
|
|
238
265
|
}
|
|
239
266
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
const
|
|
244
|
-
|
|
245
|
-
|
|
267
|
+
// Legacy COSY hosts are tried in order (api1 → api2 → api3) with the
|
|
268
|
+
// endpoint path from discovery when present. A network-level failure on
|
|
269
|
+
// one host falls through to the next; an HTTP error response surfaces as-is.
|
|
270
|
+
const cosyCandidates = candidate.endpoint_path?.startsWith("http")
|
|
271
|
+
? [candidate.endpoint_path]
|
|
272
|
+
: qoderInferenceCandidates(this.realm, auth.securityOauthToken).map(
|
|
273
|
+
(host) => `${host}${QODER_CHAT_PATH}`,
|
|
274
|
+
);
|
|
275
|
+
let response: Response | undefined;
|
|
276
|
+
let lastError: unknown;
|
|
277
|
+
for (const cosyUrl of cosyCandidates) {
|
|
278
|
+
try {
|
|
279
|
+
const attempt = () =>
|
|
280
|
+
callQoder(cosyUrl, buildQoderRequest(modelId, request, modelConfig, auth, this.realm), modelId, auth, lifecycle.signal, fetcher, version);
|
|
281
|
+
response = await attempt();
|
|
282
|
+
break;
|
|
283
|
+
} catch (err: unknown) {
|
|
284
|
+
lastError = err;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
if (response === undefined) {
|
|
288
|
+
throw lastError instanceof Error
|
|
289
|
+
? lastError
|
|
290
|
+
: new GatewayError("transport_unavailable", 502, "Qoder legacy COSY hosts are unreachable (request {requestId}). The connection ghosted us — come back in a moment 👻", {}, "upstream");
|
|
291
|
+
}
|
|
246
292
|
if (!response.ok) throw await mapUpstreamHttpError(response, "qoder");
|
|
247
293
|
if (!response.body) throw new GatewayError("platform_unavailable", 502, "Qoder returned empty body (request {requestId}). The upstream came back empty-handed — retry 🌙", {}, "upstream");
|
|
248
294
|
// Headers arrived: the pre-stream deadline has served its purpose.
|
|
@@ -250,7 +296,11 @@ class QoderAdapter implements ProviderAdapter {
|
|
|
250
296
|
// context.abort_signal) governs the body. Keeping this timer armed
|
|
251
297
|
// would kill healthy long streams at the stale pre-stream deadline.
|
|
252
298
|
lifecycle.release();
|
|
253
|
-
yield*
|
|
299
|
+
yield* this.#withSessionCacheEstimate(
|
|
300
|
+
qoderBodyToCanonicalEvents(response.body as ReadableStream<Uint8Array>, lifecycle.signal, request.model),
|
|
301
|
+
request,
|
|
302
|
+
qoderSessionId(auth.userId, modelId),
|
|
303
|
+
);
|
|
254
304
|
} catch (err: unknown) {
|
|
255
305
|
const abortError = abortGatewayError(lifecycle, err, context.abort_signal);
|
|
256
306
|
if (abortError) throw abortError;
|
|
@@ -114,6 +114,91 @@ export function qoderSessionId(userId: string, modelId: string): string {
|
|
|
114
114
|
|
|
115
115
|
type QoderFetcher = (url: string, init: RequestInit) => Promise<Response>;
|
|
116
116
|
|
|
117
|
+
/** The fields a PAT→job-token exchange response carries. */
|
|
118
|
+
export interface ExchangeQoderPatResult {
|
|
119
|
+
readonly token: string;
|
|
120
|
+
readonly name?: string;
|
|
121
|
+
readonly userType?: string;
|
|
122
|
+
readonly refreshToken?: string;
|
|
123
|
+
readonly expiresAt?: string;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* One PAT→job-token exchange against an explicit endpoint URL. Shared by
|
|
128
|
+
* `exchangeQoderPat` (auth) and the claim flow: both POST the same v1 body
|
|
129
|
+
* (`{personal_token, machine_id?, machine_token?}`) with the client identity
|
|
130
|
+
* headers and parse the same response token. Parameterized by URL so each call
|
|
131
|
+
* site keeps its own verified host (`center.qoder.sh` vs the openapi host);
|
|
132
|
+
* the header set is the one verified live against both (2026-11-05/05).
|
|
133
|
+
* Returns the raw `Response` plus the parsed result (or `null` when the body
|
|
134
|
+
* carried no usable token); `null` itself means the request failed to reach
|
|
135
|
+
* upstream. Callers decide how to surface each case (throw vs null).
|
|
136
|
+
*/
|
|
137
|
+
export async function exchangeQoderPatForHost(args: {
|
|
138
|
+
readonly pat: string;
|
|
139
|
+
readonly url: string;
|
|
140
|
+
readonly machine?: { readonly id?: string; readonly token?: string };
|
|
141
|
+
readonly fetcher: QoderFetcher;
|
|
142
|
+
readonly signal: AbortSignal;
|
|
143
|
+
readonly version?: string;
|
|
144
|
+
/** Response keys to read the job token from, tried in order. Defaults to `["token"]`. */
|
|
145
|
+
readonly tokenKeys?: readonly string[];
|
|
146
|
+
}): Promise<{ readonly response: Response; readonly result: ExchangeQoderPatResult | null } | null> {
|
|
147
|
+
const body: Record<string, unknown> = { personal_token: args.pat };
|
|
148
|
+
if (args.machine?.id) body.machine_id = args.machine.id;
|
|
149
|
+
if (args.machine?.token) body.machine_token = args.machine.token;
|
|
150
|
+
const version = args.version ?? getQoderVersion();
|
|
151
|
+
const tokenKeys = args.tokenKeys ?? ["token"];
|
|
152
|
+
let response: Response;
|
|
153
|
+
try {
|
|
154
|
+
response = await args.fetcher(args.url, {
|
|
155
|
+
method: "POST",
|
|
156
|
+
headers: {
|
|
157
|
+
"Content-Type": "application/json",
|
|
158
|
+
Accept: "application/json",
|
|
159
|
+
"User-Agent": `qoder/${version}`,
|
|
160
|
+
"X-Request-ID": randomUUID().toUpperCase(),
|
|
161
|
+
"Cosy-ClientType": "5",
|
|
162
|
+
"Cosy-Version": version,
|
|
163
|
+
"Cosy-MachineOS": qoderMachineOs(),
|
|
164
|
+
},
|
|
165
|
+
body: JSON.stringify(body),
|
|
166
|
+
signal: args.signal,
|
|
167
|
+
});
|
|
168
|
+
} catch {
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
if (!response.ok) return { response, result: null };
|
|
172
|
+
let responseBody: unknown;
|
|
173
|
+
try {
|
|
174
|
+
responseBody = await response.json();
|
|
175
|
+
} catch {
|
|
176
|
+
return { response, result: null };
|
|
177
|
+
}
|
|
178
|
+
if (typeof responseBody !== "object" || responseBody === null || Array.isArray(responseBody)) {
|
|
179
|
+
return { response, result: null };
|
|
180
|
+
}
|
|
181
|
+
const rec = responseBody as Record<string, unknown>;
|
|
182
|
+
let token = "";
|
|
183
|
+
for (const key of tokenKeys) {
|
|
184
|
+
if (typeof rec[key] === "string" && (rec[key] as string).length > 0) {
|
|
185
|
+
token = rec[key] as string;
|
|
186
|
+
break;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
if (token.length === 0) return { response, result: null };
|
|
190
|
+
return {
|
|
191
|
+
response,
|
|
192
|
+
result: {
|
|
193
|
+
token,
|
|
194
|
+
...(typeof rec["name"] === "string" ? { name: rec["name"] as string } : {}),
|
|
195
|
+
...(typeof rec["userType"] === "string" ? { userType: rec["userType"] as string } : {}),
|
|
196
|
+
...(typeof rec["refresh_token"] === "string" ? { refreshToken: rec["refresh_token"] as string } : {}),
|
|
197
|
+
...(typeof rec["expires_at"] === "string" ? { expiresAt: rec["expires_at"] as string } : {}),
|
|
198
|
+
},
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
|
|
117
202
|
/**
|
|
118
203
|
* Exchanges a PAT (`pt-…`) for a short-lived job token (`jt-…`) using the
|
|
119
204
|
* modern v1 endpoint. Verified live (2026-11-05): `POST
|
|
@@ -135,41 +220,28 @@ async function exchangeQoderPat(
|
|
|
135
220
|
realm: QoderRealm = "intl",
|
|
136
221
|
): Promise<QoderAuth> {
|
|
137
222
|
const machineId = qoderMachineIdFromPat(pat);
|
|
138
|
-
const
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
"User-Agent": `qoder/${version}`,
|
|
144
|
-
"X-Request-ID": randomUUID().toUpperCase(),
|
|
145
|
-
"Cosy-ClientType": "5",
|
|
146
|
-
"Cosy-Version": version,
|
|
147
|
-
"Cosy-MachineOS": qoderMachineOs(),
|
|
148
|
-
},
|
|
149
|
-
body: JSON.stringify({ personal_token: pat, machine_id: machineId }),
|
|
223
|
+
const exchanged = await exchangeQoderPatForHost({
|
|
224
|
+
pat,
|
|
225
|
+
url: QODER_REALMS[realm].jobTokenUrl,
|
|
226
|
+
machine: { id: machineId },
|
|
227
|
+
fetcher,
|
|
150
228
|
signal,
|
|
229
|
+
version,
|
|
151
230
|
});
|
|
152
|
-
if (
|
|
153
|
-
|
|
154
|
-
try {
|
|
155
|
-
responseBody = await response.json();
|
|
156
|
-
} catch {
|
|
157
|
-
throw new GatewayError("platform_unavailable", 502, "Qoder PAT exchange returned invalid JSON (request {requestId}). The upstream came back empty-handed — retry 🌙", {}, "upstream");
|
|
231
|
+
if (exchanged === null) {
|
|
232
|
+
throw new GatewayError("platform_unavailable", 502, "Qoder PAT exchange failed to reach upstream (request {requestId}). The upstream came back empty-handed — retry 🌙", {}, "upstream");
|
|
158
233
|
}
|
|
159
|
-
if (
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
const token = typeof rec["token"] === "string" ? (rec["token"] as string) : "";
|
|
164
|
-
if (!token) {
|
|
165
|
-
throw new GatewayError("platform_unavailable", 502, "Qoder PAT exchange returned incomplete credentials (request {requestId}). The upstream came back empty-handed — retry 🌙", {}, "upstream");
|
|
234
|
+
if (!exchanged.response.ok) throw await mapUpstreamHttpError(exchanged.response, "qoder");
|
|
235
|
+
const result = exchanged.result;
|
|
236
|
+
if (result === null) {
|
|
237
|
+
throw new GatewayError("platform_unavailable", 502, "Qoder PAT exchange returned no usable token (request {requestId}). The upstream came back empty-handed — retry 🌙", {}, "upstream");
|
|
166
238
|
}
|
|
167
239
|
return {
|
|
168
240
|
userId: machineId,
|
|
169
|
-
userName:
|
|
170
|
-
userType:
|
|
171
|
-
securityOauthToken: token,
|
|
172
|
-
refreshToken:
|
|
241
|
+
userName: result.name ?? "",
|
|
242
|
+
userType: result.userType ?? "personal_standard",
|
|
243
|
+
securityOauthToken: result.token,
|
|
244
|
+
refreshToken: result.refreshToken ?? "",
|
|
173
245
|
machineId,
|
|
174
246
|
};
|
|
175
247
|
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// Qoder session-cache estimation.
|
|
2
|
+
//
|
|
3
|
+
// The Qoder upstream never reports prompt-cache hits: neither the gRPC surface
|
|
4
|
+
// (used `prompt_tokens_details` absent on every chunk — verified live
|
|
5
|
+
// 2026-11-05, `scripts/provider-probing/qoder/direct/direct-cache-hit.ts`) nor
|
|
6
|
+
// the IDE HTTP surface (`usage: null` / `raw_usage` absent — `direct-cache-hit-ide.ts`)
|
|
7
|
+
// carries a cached-token counter. The underlying models do cache byte-identical
|
|
8
|
+
// prefixes, so for the same conversation we ESTIMATE the hit locally from the
|
|
9
|
+
// request history — exactly how the Trae and Kiro adapters bridge the same gap.
|
|
10
|
+
// This only fills the gap when upstream reports nothing (0 or unavailable); a
|
|
11
|
+
// genuine upstream report (>0) is trusted and left untouched.
|
|
12
|
+
//
|
|
13
|
+
// The state lives in a factory-created instance, so Qoder's sessions are fully
|
|
14
|
+
// isolated from Trae's/Kiro's (and any other consumer of the shared factory).
|
|
15
|
+
|
|
16
|
+
import type { CanonicalMessage, UsageRecord } from "../../../protocol/canonical-model";
|
|
17
|
+
import { createSessionCache, estimateSessionCache, resetSessionCacheForTesting } from "../shared/session-cache";
|
|
18
|
+
|
|
19
|
+
const cache = createSessionCache();
|
|
20
|
+
|
|
21
|
+
export function estimateQoderSessionCache(
|
|
22
|
+
record: UsageRecord,
|
|
23
|
+
sessionId: string | undefined,
|
|
24
|
+
messages: readonly CanonicalMessage[] | undefined,
|
|
25
|
+
): UsageRecord {
|
|
26
|
+
return estimateSessionCache(cache, record, sessionId, messages);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Drops all tracked sessions. Test-only. */
|
|
30
|
+
export function resetQoderSessionCacheForTesting(): void {
|
|
31
|
+
resetSessionCacheForTesting(cache);
|
|
32
|
+
}
|
|
@@ -12,7 +12,7 @@ import { GatewayError } from "../../../protocol/gateway-error";
|
|
|
12
12
|
import { defineModel } from "../../provider-model-definition";
|
|
13
13
|
import type { ModelDefinition } from "../../provider-model-definition";
|
|
14
14
|
import { QODER_CHAT_PATH, QODER_MODELS_PATH, qoderGatewayCandidates, type QoderRealm } from "./qoder-realm";
|
|
15
|
-
import { qoderVirtualIdFor, QODER_VIRTUAL_MODELS } from "./qoder-virtual";
|
|
15
|
+
import { qoderVirtualIdFor, QODER_PROVIDER_WARNINGS, QODER_VIRTUAL_MODELS } from "./qoder-virtual";
|
|
16
16
|
|
|
17
17
|
/** One upstream catalog row, as `/algo/api/v2/model/list` reports it. */
|
|
18
18
|
export interface QoderCatalogEntry {
|
|
@@ -199,6 +199,8 @@ function qoderModels(
|
|
|
199
199
|
// Only an explicit `true` marks the row free; an unstated flag is left
|
|
200
200
|
// unset so nothing claims a tier the roster did not state.
|
|
201
201
|
...(cfg.is_free === undefined ? {} : { free: cfg.is_free, freeTier: cfg.is_free }),
|
|
202
|
+
// Provider-level caveats, not per-model claims (D37).
|
|
203
|
+
warnings: QODER_PROVIDER_WARNINGS,
|
|
202
204
|
}),
|
|
203
205
|
);
|
|
204
206
|
}
|
|
@@ -255,6 +257,7 @@ export function qoderVirtualModels(
|
|
|
255
257
|
: target.freeTier === undefined
|
|
256
258
|
? {}
|
|
257
259
|
: { free: target.freeTier, freeTier: target.freeTier }),
|
|
260
|
+
warnings: QODER_PROVIDER_WARNINGS,
|
|
258
261
|
}),
|
|
259
262
|
);
|
|
260
263
|
}
|
|
@@ -319,7 +322,7 @@ export function mapQoderCatalogResponse(raw: unknown): QoderCatalogEntry[] {
|
|
|
319
322
|
* to a base-catalog/majority vote that can sit below the roster's ceiling
|
|
320
323
|
* (e.g. `qwen3.7-plus` votes 64k while the roster says 131k).
|
|
321
324
|
*/
|
|
322
|
-
function
|
|
325
|
+
export function qoderRosterMaxOutput(providerId: string, key: string): number | undefined {
|
|
323
326
|
const configs = providerId === "qodercn" ? QODER_CN_MODEL_CONFIGS : QODER_INTL_MODEL_CONFIGS;
|
|
324
327
|
return configs[key]?.max_output_tokens;
|
|
325
328
|
}
|
|
@@ -329,7 +332,7 @@ export function qoderCatalogModel(
|
|
|
329
332
|
entry: QoderCatalogEntry,
|
|
330
333
|
providerId: string,
|
|
331
334
|
): ModelDefinition {
|
|
332
|
-
const rosterOutput =
|
|
335
|
+
const rosterOutput = qoderRosterMaxOutput(providerId, entry.key);
|
|
333
336
|
return defineModel({
|
|
334
337
|
id: entry.key,
|
|
335
338
|
providerId,
|
|
@@ -347,8 +350,9 @@ export function qoderCatalogModel(
|
|
|
347
350
|
free: entry.isFree,
|
|
348
351
|
freeTier: entry.isFree,
|
|
349
352
|
// The live row carries no output ceiling, so inherit the static roster's
|
|
350
|
-
// stated one when it exists (see `
|
|
353
|
+
// stated one when it exists (see `qoderRosterMaxOutput`).
|
|
351
354
|
...(rosterOutput === undefined ? {} : { out: rosterOutput }),
|
|
355
|
+
warnings: QODER_PROVIDER_WARNINGS,
|
|
352
356
|
});
|
|
353
357
|
}
|
|
354
358
|
|
|
@@ -442,6 +446,21 @@ export function qoderDiscoveredMaxContexts(realm: QoderRealm): ReadonlyMap<strin
|
|
|
442
446
|
return discoveryMaxContextByKey.get(realm) ?? new Map();
|
|
443
447
|
}
|
|
444
448
|
|
|
449
|
+
/**
|
|
450
|
+
* The widest selectable context window for one model key.
|
|
451
|
+
*
|
|
452
|
+
* The live list's `context_config` is the authority when discovery has run:
|
|
453
|
+
* it names every window the account can switch to, and the widest one is what
|
|
454
|
+
* a dispatch should send (`model_config.max_input_tokens`) so a conversation is
|
|
455
|
+
* never capped at the *currently active* window (often 180k) when the account
|
|
456
|
+
* can use 1M. Falls back to the static roster's stated window otherwise.
|
|
457
|
+
*/
|
|
458
|
+
export function qoderMaxContextFor(modelId: string, modelConfig: QoderModelConfig, realm: QoderRealm): number {
|
|
459
|
+
const discovered = qoderDiscoveredMaxContexts(realm).get(modelId);
|
|
460
|
+
if (discovered !== undefined) return discovered;
|
|
461
|
+
return modelConfig.max_input_tokens;
|
|
462
|
+
}
|
|
463
|
+
|
|
445
464
|
export async function discoverQoderModels(input: {
|
|
446
465
|
readonly realm: QoderRealm;
|
|
447
466
|
/** Account secret the injected signer signs with; blank fails fast. */
|
|
@@ -522,7 +541,7 @@ export async function discoverQoderModels(input: {
|
|
|
522
541
|
// is published.
|
|
523
542
|
const virtuals = QODER_VIRTUAL_MODELS.filter((virtual) => virtual.target === entry.key);
|
|
524
543
|
if (virtuals.length === 0) return [qoderCatalogModel(entry, providerId)];
|
|
525
|
-
const rosterOutput =
|
|
544
|
+
const rosterOutput = qoderRosterMaxOutput(providerId, entry.key);
|
|
526
545
|
return virtuals.map((virtual) =>
|
|
527
546
|
defineModel({
|
|
528
547
|
id: virtual.id,
|
|
@@ -534,7 +553,7 @@ export async function discoverQoderModels(input: {
|
|
|
534
553
|
reasoning: entry.isReasoning,
|
|
535
554
|
// A virtual override wins; otherwise inherit the static roster's
|
|
536
555
|
// ceiling for the slot instead of a base-catalog/majority estimate
|
|
537
|
-
// (see `
|
|
556
|
+
// (see `qoderRosterMaxOutput`).
|
|
538
557
|
...(virtual.outputLimit !== undefined
|
|
539
558
|
? { out: virtual.outputLimit }
|
|
540
559
|
: rosterOutput === undefined
|
|
@@ -542,6 +561,7 @@ export async function discoverQoderModels(input: {
|
|
|
542
561
|
: { out: rosterOutput }),
|
|
543
562
|
free: virtual.free ?? entry.isFree,
|
|
544
563
|
freeTier: virtual.free ?? entry.isFree,
|
|
564
|
+
warnings: QODER_PROVIDER_WARNINGS,
|
|
545
565
|
}),
|
|
546
566
|
);
|
|
547
567
|
});
|