cartethyia 2.7.2 → 2.7.4
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 +2 -0
- package/dist/dashboard/assets/{BannedPage-XaUmiBrC.js → BannedPage-ChG5svFb.js} +1 -1
- package/dist/dashboard/assets/BannedPage-ChG5svFb.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-BIcWQun7.js +1 -0
- package/dist/dashboard/assets/CliToolDetailPage-BIcWQun7.js.gz +0 -0
- package/dist/dashboard/assets/CliToolsPage-Bf2VelDe.js +1 -0
- package/dist/dashboard/assets/CliToolsPage-Bf2VelDe.js.gz +0 -0
- package/dist/dashboard/assets/{CombosPage-CXGS_Ct-.js → CombosPage-DCgm5g6g.js} +1 -1
- package/dist/dashboard/assets/CombosPage-DCgm5g6g.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-a8mD0QF1.js +1 -0
- package/dist/dashboard/assets/ConsoleLogPage-a8mD0QF1.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-ggeMSjte.js +1 -0
- package/dist/dashboard/assets/CustomizationPage-ggeMSjte.js.gz +0 -0
- package/dist/dashboard/assets/{HealthEventsModal-BKtAPYVF.js → HealthEventsModal-DzSWzE_j.js} +1 -1
- package/dist/dashboard/assets/HealthEventsModal-DzSWzE_j.js.gz +0 -0
- package/dist/dashboard/assets/{LoginPage-BdmRLnq2.js → LoginPage-U6AKaJcF.js} +1 -1
- package/dist/dashboard/assets/LoginPage-U6AKaJcF.js.gz +0 -0
- package/dist/dashboard/assets/{ModelPicker-BH8Tehdi.js → ModelPicker-BMS2t8BA.js} +1 -1
- package/dist/dashboard/assets/ModelPicker-BMS2t8BA.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-D2D4IiHl.js +1 -0
- package/dist/dashboard/assets/OverviewPage-D2D4IiHl.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-CrNf7j_X.js +5 -0
- package/dist/dashboard/assets/ProviderDetailPage-CrNf7j_X.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-BSDbquc1.js +1 -0
- package/dist/dashboard/assets/ProvidersPage-BSDbquc1.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-lsrx7NYg.js +9 -0
- package/dist/dashboard/assets/ProxyPage-lsrx7NYg.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-L0skzML8.js +1 -0
- package/dist/dashboard/assets/QuotaPage-L0skzML8.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-CmAbjRHW.js +1 -0
- package/dist/dashboard/assets/SettingsPage-CmAbjRHW.js.gz +0 -0
- package/dist/dashboard/assets/SetupPage-f0DE1h0-.js +1 -0
- package/dist/dashboard/assets/SetupPage-f0DE1h0-.js.gz +0 -0
- package/dist/dashboard/assets/{SortableList-Dqf4zpS-.js → SortableList-ClzpwyET.js} +1 -1
- package/dist/dashboard/assets/SortableList-ClzpwyET.js.gz +0 -0
- package/dist/dashboard/assets/{StudioPage-Byj2lAEm.js → StudioPage-Cq5tW7hW.js} +3 -3
- package/dist/dashboard/assets/StudioPage-Cq5tW7hW.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-Dfc9rWQn.js +56 -0
- package/dist/dashboard/assets/UsagePage-Dfc9rWQn.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-C38iXoO4.js +1 -0
- package/dist/dashboard/assets/api-keys-C38iXoO4.js.gz +0 -0
- package/dist/dashboard/assets/app-a159k0ub.css +2 -0
- package/dist/dashboard/assets/app-a159k0ub.css.gz +0 -0
- package/dist/dashboard/assets/arrow-up-59ms1iiT.js +1 -0
- package/dist/dashboard/assets/arrow-up-59ms1iiT.js.gz +0 -0
- package/dist/dashboard/assets/assert-DbRA8EG2.js +15 -0
- package/dist/dashboard/assets/assert-DbRA8EG2.js.gz +0 -0
- package/dist/dashboard/assets/{badge-DC2L43Mj.js → badge-D9CS6dZk.js} +1 -1
- package/dist/dashboard/assets/badge-D9CS6dZk.js.gz +0 -0
- package/dist/dashboard/assets/{banner-BBVcb0J3.js → banner-DympBNq4.js} +1 -1
- package/dist/dashboard/assets/banner-DympBNq4.js.gz +0 -0
- package/dist/dashboard/assets/{card-Cca1XC6G.js → card-ibRWU_uT.js} +1 -1
- package/dist/dashboard/assets/card-ibRWU_uT.js.gz +0 -0
- package/dist/dashboard/assets/cli-tools-63qBGaRw.js +1 -0
- package/dist/dashboard/assets/cli-tools-63qBGaRw.js.gz +0 -0
- package/dist/dashboard/assets/{clipboard-button-DUzEbynq.js → clipboard-button-DJm-9pz7.js} +1 -1
- package/dist/dashboard/assets/clipboard-button-DJm-9pz7.js.gz +0 -0
- package/dist/dashboard/assets/{dialog-DFvWhwTa.js → dialog-8fiE6tec.js} +1 -1
- package/dist/dashboard/assets/dialog-8fiE6tec.js.gz +0 -0
- package/dist/dashboard/assets/drawer-CqGYeOrd.js +1 -0
- package/dist/dashboard/assets/drawer-CqGYeOrd.js.gz +0 -0
- package/dist/dashboard/assets/{entry-Bqy8RFvM.js → entry-BSAsYsmV.js} +1 -1
- package/dist/dashboard/assets/entry-BSAsYsmV.js.gz +0 -0
- package/dist/dashboard/assets/entry-CiIQULZD.js +4 -0
- package/dist/dashboard/assets/entry-CiIQULZD.js.gz +0 -0
- package/dist/dashboard/assets/entry-Daz0lJ-W.js +1 -0
- package/dist/dashboard/assets/entry-Daz0lJ-W.js.gz +0 -0
- package/dist/dashboard/assets/{concurrency-BDF0ok7y.js → fingerprint-pattern-BWuHtbW4.js} +1 -1
- package/dist/dashboard/assets/fingerprint-pattern-BWuHtbW4.js.gz +0 -0
- package/dist/dashboard/assets/{formatters-CWWZ-uCZ.js → formatters-D-MTJxMr.js} +1 -1
- package/dist/dashboard/assets/formatters-D-MTJxMr.js.gz +0 -0
- package/dist/dashboard/assets/{github-badge-C9e8Ts7e.js → github-badge-CPK47xQx.js} +1 -1
- package/dist/dashboard/assets/github-badge-CPK47xQx.js.gz +0 -0
- package/dist/dashboard/assets/{index-DFVFrnWW.js → index-DBNPOfhV.js} +2 -2
- package/dist/dashboard/assets/index-DBNPOfhV.js.gz +0 -0
- package/dist/dashboard/assets/{inline-BFUcvllj.js → inline-BT0zntEr.js} +1 -1
- package/dist/dashboard/assets/inline-BT0zntEr.js.gz +0 -0
- package/dist/dashboard/assets/{input-C_ILAvBb.js → input-Da2ZViWg.js} +1 -1
- package/dist/dashboard/assets/input-Da2ZViWg.js.gz +0 -0
- package/dist/dashboard/assets/{layout-CBIyT6Rt.js → layout-BXNeLw6M.js} +1 -1
- package/dist/dashboard/assets/layout-BXNeLw6M.js.gz +0 -0
- package/dist/dashboard/assets/{list-row-ebJ_-Nsb.js → list-row-BycOrrIV.js} +1 -1
- package/dist/dashboard/assets/list-row-BycOrrIV.js.gz +0 -0
- package/dist/dashboard/assets/live-DCQNZpF7.js +1 -0
- package/dist/dashboard/assets/live-DCQNZpF7.js.gz +0 -0
- package/dist/dashboard/assets/minimize-2-BTmMTK4o.js +1 -0
- package/dist/dashboard/assets/minimize-2-BTmMTK4o.js.gz +0 -0
- package/dist/dashboard/assets/{mutation-sMD965oO.js → mutation-Rr0TIvcj.js} +1 -1
- package/dist/dashboard/assets/mutation-Rr0TIvcj.js.gz +0 -0
- package/dist/dashboard/assets/network-9Yj2ZyLo.js +1 -0
- package/dist/dashboard/assets/network-9Yj2ZyLo.js.gz +0 -0
- package/dist/dashboard/assets/{numbers-BO9dKaFr.js → numbers-CEagj6ZZ.js} +2 -2
- package/dist/dashboard/assets/numbers-CEagj6ZZ.js.gz +0 -0
- package/dist/dashboard/assets/{query-keys-CfL_Zdoh.js → query-keys-BloKtaXJ.js} +1 -1
- package/dist/dashboard/assets/query-keys-BloKtaXJ.js.gz +0 -0
- package/dist/dashboard/assets/quota-Caw7wqci.js +1 -0
- package/dist/dashboard/assets/quota-Caw7wqci.js.gz +0 -0
- package/dist/dashboard/assets/routing-CO9zCUus.js +1 -0
- package/dist/dashboard/assets/routing-CO9zCUus.js.gz +0 -0
- package/dist/dashboard/assets/{select-DBNVLdUu.js → select-BIYIBkMU.js} +2 -2
- package/dist/dashboard/assets/select-BIYIBkMU.js.gz +0 -0
- package/dist/dashboard/assets/settings-2-Ds9DQp0t.js +1 -0
- package/dist/dashboard/assets/settings-2-Ds9DQp0t.js.gz +0 -0
- package/dist/dashboard/assets/settings-jS3-PNSA.js +1 -0
- package/dist/dashboard/assets/settings-jS3-PNSA.js.gz +0 -0
- package/dist/dashboard/assets/{stack-EMrOMt4I.js → stack-DfgnFDWY.js} +1 -1
- package/dist/dashboard/assets/stack-DfgnFDWY.js.gz +0 -0
- package/dist/dashboard/assets/{state-CWoo7ty5.js → state-CkfdYWel.js} +1 -1
- package/dist/dashboard/assets/state-CkfdYWel.js.gz +0 -0
- package/dist/dashboard/assets/{switch-CCjT6IkF.js → switch-SYm237HG.js} +1 -1
- package/dist/dashboard/assets/switch-SYm237HG.js.gz +0 -0
- package/dist/dashboard/assets/{useMutation-DjkLHUcE.js → useMutation-BQqCRNIi.js} +1 -1
- package/dist/dashboard/assets/useMutation-BQqCRNIi.js.gz +0 -0
- package/dist/dashboard/index.html +3 -2
- package/dist/dashboard/index.html.gz +0 -0
- package/dist/dashboard/precompressed-assets.json +118 -115
- package/migrations/full-postgres/0026_add_qoder_native_identity.sql +20 -0
- package/migrations/full-postgres/0027_qoder_identity_genuine_seed.sql +21 -0
- package/migrations/full-postgres/0028_add_family_template_key_telemetry.sql +40 -0
- package/migrations/full-postgres/0029_null_share_child_policy_columns.sql +38 -0
- package/migrations/full-postgres/0030_add_api_key_max_children.sql +10 -0
- package/migrations/full-postgres/0031_tenant_system_prompts.sql +16 -0
- package/migrations/full-postgres/0032_add_provider_pool_accounts.sql +53 -0
- package/migrations/full-postgres/0033_add_api_key_ban.sql +9 -0
- package/migrations/full-postgres/0034_add_api_key_ban_until.sql +7 -0
- package/migrations/full-postgres/0035_align_pool_account_status_enum.sql +21 -0
- package/migrations/lite-sqlite3/0026_add_qoder_native_identity.sql +15 -0
- package/migrations/lite-sqlite3/0027_qoder_identity_genuine_seed.sql +21 -0
- package/migrations/lite-sqlite3/0028_add_family_template_key_telemetry.sql +40 -0
- package/migrations/lite-sqlite3/0029_null_share_child_policy_columns.sql +38 -0
- package/migrations/lite-sqlite3/0030_add_api_key_max_children.sql +10 -0
- package/migrations/lite-sqlite3/0031_tenant_system_prompts.sql +10 -0
- package/migrations/lite-sqlite3/0032_add_provider_pool_accounts.sql +44 -0
- package/migrations/lite-sqlite3/0033_add_api_key_ban.sql +8 -0
- package/migrations/lite-sqlite3/0034_add_api_key_ban_until.sql +5 -0
- package/migrations/lite-sqlite3/0035_align_pool_account_status_enum.sql +6 -0
- package/package.json +2 -1
- package/src/boot/app.ts +9 -8
- package/src/config/network.ts +7 -3
- package/src/console/api-keys/bucket-spend.ts +7 -7
- package/src/console/api-keys/contracts.ts +475 -410
- package/src/console/api-keys/routes.ts +211 -23
- package/src/console/audit/routes.ts +3 -3
- package/src/console/auth/account-reset.ts +4 -4
- package/src/console/auth/session.ts +3 -3
- package/src/console/backup/contracts.ts +2 -0
- package/src/console/backup/export-endpoint.ts +14 -17
- package/src/console/backup/routes.ts +43 -11
- package/src/console/backup/service.ts +11 -6
- package/src/console/backup/store.ts +384 -88
- package/src/console/cli-tools/routes.ts +3 -3
- package/src/console/console-router-guards.ts +2 -2
- package/src/console/console-router.ts +4 -4
- package/src/console/dashboard-assets.ts +16 -1
- package/src/console/data-management/routes.ts +3 -3
- package/src/console/domain-registration.ts +4 -2
- package/src/console/observability/contracts.ts +4 -0
- package/src/console/observability/live.ts +3 -3
- package/src/console/observability/logs.ts +3 -3
- package/src/console/observability/performance.ts +3 -3
- package/src/console/observability/routes.ts +3 -3
- package/src/console/observability/store.ts +16 -16
- package/src/console/providers/catalog/contracts.ts +43 -22
- package/src/console/providers/catalog/provider-operations.ts +17 -2
- package/src/console/providers/catalog/routes.ts +3 -3
- package/src/console/providers/catalog/store-helpers.ts +349 -326
- package/src/console/providers/catalog/store.ts +1523 -1292
- package/src/console/providers/detail/routes.ts +3 -3
- package/src/console/providers/oauth/routes.ts +3 -3
- package/src/console/quota/account-quota-tenant.ts +34 -2
- package/src/console/quota/account-quota.ts +4 -1
- package/src/console/quota/account-test-egress.ts +4 -4
- package/src/console/quota/native-identity.ts +189 -0
- package/src/console/remote-support/contracts.ts +12 -6
- package/src/console/remote-support/service.ts +2 -2
- package/src/console/routing/model/routes.ts +3 -3
- package/src/console/routing/pools/contracts.ts +11 -8
- package/src/console/routing/pools/operations.ts +14 -7
- package/src/console/routing/pools/probe-verdict.ts +8 -3
- package/src/console/routing/pools/relay-deploy.ts +9 -9
- package/src/console/routing/pools/routes.ts +3 -3
- package/src/console/routing/pools/store.ts +21 -1
- package/src/console/settings/routes.ts +3 -3
- package/src/console/share/share-router.ts +51 -18
- package/src/console/share/share-stats.ts +158 -49
- package/src/console/share/share-usage.ts +24 -7
- package/src/console/shared/read-through-cache.ts +38 -2
- package/src/console/system-prompt/contracts.ts +102 -0
- package/src/console/system-prompt/routes.ts +165 -0
- package/src/console/updates/routes.ts +7 -4
- package/src/main.ts +11 -0
- package/src/network/outbound-fetch.ts +75 -4
- package/src/network/pool/resolver.ts +39 -39
- package/src/network/pool-cooldown-store.ts +71 -16
- package/src/network/pool-health-machine.ts +21 -1
- package/src/network/rate-limit-verdict.ts +68 -0
- package/src/network/ssrf.ts +4 -4
- package/src/observability/event-query.ts +437 -22
- package/src/observability/telemetry-status.ts +1 -1
- package/src/persistence/db-pg.ts +42 -0
- package/src/persistence/db-sqlite.ts +7 -2
- package/src/persistence/readiness.ts +18 -6
- package/src/persistence/schema.ts +162 -1
- package/src/persistence/stores/api-key-store.ts +849 -581
- package/src/persistence/stores/share-store.ts +65 -13
- package/src/persistence/stores/telemetry-store.ts +58 -2
- package/src/persistence/system-prompt-store.ts +104 -0
- package/src/protocol/codecs.ts +6 -6
- package/src/protocol/gateway-error.ts +6 -3
- package/src/protocol/image-generation.ts +2 -2
- package/src/protocol/reasoning.ts +65 -1
- package/src/protocol/request/chat.ts +1 -0
- package/src/protocol/response/chat.ts +2 -2
- package/src/protocol/response/codex.ts +1 -1
- package/src/protocol/response/gemini.ts +1 -1
- package/src/protocol/response/messages.ts +5 -5
- package/src/protocol/response/responses.ts +2 -2
- package/src/protocol/sse-decoder.ts +14 -3
- package/src/protocol/translation/capabilities.ts +2 -2
- package/src/protocol/upstream/send-claude-messages.ts +3 -3
- package/src/providers/adapters/base-adapter.ts +1 -1
- package/src/providers/adapters/compatible-adapter.ts +2 -2
- package/src/providers/authentication/oauth-refresh-service.ts +2 -2
- package/src/providers/configured-provider.ts +1 -1
- package/src/providers/discovery/probe-phases.ts +2 -2
- package/src/providers/discovery/probing-service.ts +12 -12
- package/src/providers/integrations/agentrouter/agentrouter.ts +2 -3
- package/src/providers/integrations/alibabacp/alibabacp.ts +2 -2
- package/src/providers/integrations/anthropic.ts +4 -11
- package/src/providers/integrations/antigravity/antigravity.ts +33 -47
- package/src/providers/integrations/buddy/buddy-catalog-shared.ts +1 -1
- package/src/providers/integrations/buddy/buddy-chat-shared.ts +30 -2
- package/src/providers/integrations/buddy/buddy-discovery-shared.ts +37 -6
- package/src/providers/integrations/buddy/buddy-identity.ts +22 -1
- package/src/providers/integrations/buddy/buddy-media.ts +21 -17
- package/src/providers/integrations/buddy/codebuddy/provider.ts +6 -2
- package/src/providers/integrations/buddy/workbuddy/provider.ts +6 -2
- package/src/providers/integrations/claude/claude-betas.ts +1 -1
- package/src/providers/integrations/claude/claude-credentials.ts +1 -1
- package/src/providers/integrations/claude/claude-slow-lane-retry.ts +1 -1
- package/src/providers/integrations/claude/claude.ts +3 -10
- package/src/providers/integrations/cline/cline.ts +8 -1
- package/src/providers/integrations/cloudflareai/cloudflareai.ts +4 -7
- package/src/providers/integrations/codex/codex-errors.ts +1 -1
- package/src/providers/integrations/codex/codex.ts +14 -15
- package/src/providers/integrations/commandcode/commandcode.ts +4 -4
- package/src/providers/integrations/commandcode/quota.ts +5 -1
- package/src/providers/integrations/cursor/cursor-http2.ts +11 -11
- package/src/providers/integrations/cursor/cursor.ts +15 -15
- package/src/providers/integrations/factory/factory.ts +4 -4
- package/src/providers/integrations/gemini.ts +11 -11
- package/src/providers/integrations/github/github.ts +26 -7
- package/src/providers/integrations/grok/grok.ts +2 -6
- package/src/providers/integrations/hermes.ts +5 -1
- package/src/providers/integrations/inferhub.ts +4 -11
- package/src/providers/integrations/kilo/kilo.ts +4 -0
- package/src/providers/integrations/kimi/kimi.ts +4 -11
- package/src/providers/integrations/kiro/kiro.ts +10 -10
- package/src/providers/integrations/muse/muse.ts +6 -9
- package/src/providers/integrations/openrouter/provider.ts +4 -0
- package/src/providers/integrations/perplexity.ts +2 -2
- package/src/providers/integrations/qoder/qoder-catalog.ts +47 -9
- package/src/providers/integrations/qoder/qoder-checkin.ts +242 -51
- package/src/providers/integrations/qoder/qoder-native-identity-store.ts +157 -0
- package/src/providers/integrations/qoder/qoder-oauth.ts +12 -3
- package/src/providers/integrations/qoder/qoder-realm.ts +15 -0
- package/src/providers/integrations/qoder/qoder-umid.ts +287 -0
- package/src/providers/integrations/qoder/qoder-virtual.ts +2 -2
- package/src/providers/integrations/qoder/qoder.ts +192 -24
- package/src/providers/integrations/tokenharbor.ts +2 -2
- package/src/providers/integrations/trae/trae.ts +6 -9
- package/src/providers/integrations/zcode/oauth.ts +1 -1
- package/src/providers/operations/account-error-classification.ts +6 -8
- package/src/providers/operations/account-health-service.ts +14 -16
- package/src/providers/operations/client-versions.ts +37 -22
- package/src/providers/operations/provider-catalog-service.ts +4 -4
- package/src/providers/operations/provider-credential-service.ts +16 -4
- package/src/providers/operations/upstream-deadline.ts +3 -3
- package/src/providers/provider-contracts.ts +55 -3
- package/src/providers/provider-credential-resolver.ts +1 -1
- package/src/providers/provider-registry.ts +4 -4
- package/src/providers/quota/quota-contracts.ts +24 -10
- package/src/providers/quota/refresh.ts +2 -2
- package/src/providers/search/search-provider.ts +5 -5
- package/src/security/abuse.ts +4 -4
- package/src/security/access-control.ts +19 -0
- package/src/security/admission/reasons.ts +17 -17
- package/src/security/api-key-auth.ts +18 -5
- package/src/shared/coerce.ts +19 -0
- package/src/transport/dispatch/attempt-finalize.ts +4 -3
- package/src/transport/dispatch/attempt-loop.ts +10 -7
- package/src/transport/dispatch/empty-stream.ts +6 -6
- package/src/transport/dispatch/generation.ts +9 -9
- package/src/transport/dispatch/leases.ts +8 -8
- package/src/transport/dispatch/proxy-request.ts +38 -9
- package/src/transport/dispatch/responses-compact.ts +11 -11
- package/src/transport/dispatch/retry-policy.ts +4 -67
- package/src/transport/dispatch/stream-bridge.ts +1 -1
- package/src/transport/dispatch/streaming/finalize.ts +9 -8
- package/src/transport/dispatch/streaming/stream.ts +16 -1
- package/src/transport/dispatch/systemone.ts +8 -8
- package/src/transport/dispatch/token-count.ts +9 -9
- package/src/transport/dispatch/upstream.ts +10 -0
- package/src/transport/dispatch/websearch.ts +6 -6
- package/src/transport/drain-endpoint.ts +2 -2
- package/src/transport/failure-policy.ts +13 -6
- package/src/transport/guards/auth-guard.ts +6 -5
- package/src/transport/guards/body-policy.ts +4 -4
- package/src/transport/guards/error-lifecycle.ts +18 -3
- package/src/transport/guards/guard-errors.ts +7 -3
- package/src/transport/guards/headroom-guard.ts +1 -1
- package/src/transport/guards/pipeline.ts +6 -6
- package/src/transport/guards/readiness-guard.ts +16 -2
- package/src/transport/guards/request-context.ts +3 -3
- package/src/transport/request/preparer.ts +37 -21
- package/src/transport/request/state.ts +4 -7
- package/src/transport/request/system-prompt-injection.ts +89 -0
- package/src/transport/request/tenant-preferences.ts +43 -0
- package/src/transport/routing/admission.ts +13 -3
- package/src/transport/routing/route-catalog.ts +104 -2
- package/src/transport/routing/route-model.ts +60 -7
- package/src/transport/routing/router.ts +306 -256
- package/src/transport/shutdown-notice.ts +19 -7
- package/src/transport/surface/completion.ts +1 -1
- package/src/transport/surface/messages/encode.ts +1 -1
- package/src/workers/daily-checkin.ts +2 -0
- package/dist/dashboard/assets/BannedPage-XaUmiBrC.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-CqD7QiLM.js +0 -1
- package/dist/dashboard/assets/CliToolDetailPage-CqD7QiLM.js.gz +0 -0
- package/dist/dashboard/assets/CliToolsPage-0XUo4UMn.js +0 -1
- package/dist/dashboard/assets/CliToolsPage-0XUo4UMn.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-CXGS_Ct-.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-DGnGNYnM.js +0 -1
- package/dist/dashboard/assets/ConsoleLogPage-DGnGNYnM.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-DAG5J9EY.js +0 -1
- package/dist/dashboard/assets/CustomizationPage-DAG5J9EY.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-BKtAPYVF.js.gz +0 -0
- package/dist/dashboard/assets/LoginPage-BdmRLnq2.js.gz +0 -0
- package/dist/dashboard/assets/ModelPicker-BH8Tehdi.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-CqMxibJB.js +0 -1
- package/dist/dashboard/assets/OverviewPage-CqMxibJB.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-CqMh2NRX.js +0 -4
- package/dist/dashboard/assets/ProviderDetailPage-CqMh2NRX.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-B9uWu4Nz.js +0 -1
- package/dist/dashboard/assets/ProvidersPage-B9uWu4Nz.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-BoWOhpDO.js +0 -9
- package/dist/dashboard/assets/ProxyPage-BoWOhpDO.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-7P2n--Fv.js +0 -1
- package/dist/dashboard/assets/QuotaPage-7P2n--Fv.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-CxZ1fTkN.js +0 -1
- package/dist/dashboard/assets/SettingsPage-CxZ1fTkN.js.gz +0 -0
- package/dist/dashboard/assets/SetupPage-Co12Zzaw.js +0 -1
- package/dist/dashboard/assets/SetupPage-Co12Zzaw.js.gz +0 -0
- package/dist/dashboard/assets/SortableList-Dqf4zpS-.js.gz +0 -0
- package/dist/dashboard/assets/StudioPage-Byj2lAEm.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-CTkLAuRc.js +0 -56
- package/dist/dashboard/assets/UsagePage-CTkLAuRc.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-CUi6UXsR.js +0 -1
- package/dist/dashboard/assets/api-keys-CUi6UXsR.js.gz +0 -0
- package/dist/dashboard/assets/app-KvsUJUMF.css +0 -2
- package/dist/dashboard/assets/app-KvsUJUMF.css.gz +0 -0
- package/dist/dashboard/assets/assert-67xXH5rf.js +0 -1
- package/dist/dashboard/assets/assert-67xXH5rf.js.gz +0 -0
- package/dist/dashboard/assets/badge-DC2L43Mj.js.gz +0 -0
- package/dist/dashboard/assets/banner-BBVcb0J3.js.gz +0 -0
- package/dist/dashboard/assets/card-Cca1XC6G.js.gz +0 -0
- package/dist/dashboard/assets/cli-tools-KvsWa1Jl.js +0 -1
- package/dist/dashboard/assets/cli-tools-KvsWa1Jl.js.gz +0 -0
- package/dist/dashboard/assets/clipboard-button-DUzEbynq.js.gz +0 -0
- package/dist/dashboard/assets/concurrency-BDF0ok7y.js.gz +0 -0
- package/dist/dashboard/assets/dialog-DFvWhwTa.js.gz +0 -0
- package/dist/dashboard/assets/drawer-CFM2sMX1.js +0 -1
- package/dist/dashboard/assets/drawer-CFM2sMX1.js.gz +0 -0
- package/dist/dashboard/assets/entry-Bqy8RFvM.js.gz +0 -0
- package/dist/dashboard/assets/entry-ClCMCWWQ.js +0 -4
- package/dist/dashboard/assets/entry-ClCMCWWQ.js.gz +0 -0
- package/dist/dashboard/assets/entry-_xHo5x6r.js +0 -1
- package/dist/dashboard/assets/entry-_xHo5x6r.js.gz +0 -0
- package/dist/dashboard/assets/formatters-CWWZ-uCZ.js.gz +0 -0
- package/dist/dashboard/assets/github-badge-C9e8Ts7e.js.gz +0 -0
- package/dist/dashboard/assets/index-DFVFrnWW.js.gz +0 -0
- package/dist/dashboard/assets/inline-BFUcvllj.js.gz +0 -0
- package/dist/dashboard/assets/input-C_ILAvBb.js.gz +0 -0
- package/dist/dashboard/assets/layout-CBIyT6Rt.js.gz +0 -0
- package/dist/dashboard/assets/list-row-ebJ_-Nsb.js.gz +0 -0
- package/dist/dashboard/assets/live-D0mm--ME.js +0 -1
- package/dist/dashboard/assets/live-D0mm--ME.js.gz +0 -0
- package/dist/dashboard/assets/minimize-2-D-MN8W8a.js +0 -1
- package/dist/dashboard/assets/minimize-2-D-MN8W8a.js.gz +0 -0
- package/dist/dashboard/assets/mutation-sMD965oO.js.gz +0 -0
- package/dist/dashboard/assets/numbers-BO9dKaFr.js.gz +0 -0
- package/dist/dashboard/assets/query-keys-CfL_Zdoh.js.gz +0 -0
- package/dist/dashboard/assets/quota-DZGyp0N5.js +0 -1
- package/dist/dashboard/assets/quota-DZGyp0N5.js.gz +0 -0
- package/dist/dashboard/assets/routing-BnIpi1IJ.js +0 -1
- package/dist/dashboard/assets/routing-BnIpi1IJ.js.gz +0 -0
- package/dist/dashboard/assets/select-DBNVLdUu.js.gz +0 -0
- package/dist/dashboard/assets/settings-BAFhDgX8.js +0 -1
- package/dist/dashboard/assets/settings-BAFhDgX8.js.gz +0 -0
- package/dist/dashboard/assets/stack-EMrOMt4I.js.gz +0 -0
- package/dist/dashboard/assets/state-CWoo7ty5.js.gz +0 -0
- package/dist/dashboard/assets/switch-CCjT6IkF.js.gz +0 -0
- package/dist/dashboard/assets/useMutation-DjkLHUcE.js.gz +0 -0
- /package/dist/dashboard/assets/{app-BaWd0puM.js → app-BayTTy01.js} +0 -0
- /package/dist/dashboard/assets/{app-BaWd0puM.js.gz → app-BayTTy01.js.gz} +0 -0
|
@@ -23,14 +23,19 @@ import type {
|
|
|
23
23
|
import { toolResultParts, toolCallParts } from "../../../protocol/canonical-model";
|
|
24
24
|
import { decodeSseEvents } from "../../../protocol/sse-decoder";
|
|
25
25
|
import { usageFromProvider, readReasoningText } from "../../provider-usage";
|
|
26
|
-
import { readCredentialSecret, type ProviderDispatchTarget, type ProviderAdapter, type ProviderDispatchContext, type ProviderId } from "../../provider-contracts";
|
|
26
|
+
import { readCredentialSecret, assertWireFamily, type ProviderDispatchTarget, type ProviderAdapter, type ProviderDispatchContext, type ProviderId } from "../../provider-contracts";
|
|
27
27
|
import type { ModelDefinition } from "../../provider-model-definition";
|
|
28
28
|
import { parseQoderOAuthState } from "./qoder-oauth";
|
|
29
29
|
import { getQoderVersion } from "../../operations/client-versions";
|
|
30
30
|
import { abortGatewayError, createUpstreamDeadlineLifecycle } from "../../operations/upstream-deadline";
|
|
31
31
|
import { qoderMachineOs } from "../../operations/cli-platform";
|
|
32
|
-
import { qoderInferenceCandidates, QODER_CHAT_PATH, QODER_REALMS, type QoderRealm } from "./qoder-realm";
|
|
33
|
-
import {
|
|
32
|
+
import { qoderIdeChatUrl, qoderInferenceCandidates, QODER_CHAT_PATH, QODER_REALMS, type QoderRealm } from "./qoder-realm";
|
|
33
|
+
import {
|
|
34
|
+
QODER_CN_MODEL_CONFIGS,
|
|
35
|
+
QODER_INTL_MODEL_CONFIGS,
|
|
36
|
+
QODER_INTL_MODELS,
|
|
37
|
+
qoderDiscoveredMaxContexts,
|
|
38
|
+
} from "./qoder-catalog";
|
|
34
39
|
import { isQoderVirtualModel, qoderDispatchModelId } from "./qoder-virtual";
|
|
35
40
|
import {
|
|
36
41
|
LEAK_MARKER_ANY_RE,
|
|
@@ -46,7 +51,7 @@ const LEAK_HOLD_MAX_CHARS = 32_768;
|
|
|
46
51
|
const LEAK_HOLD_MAX_FRAMES = 200;
|
|
47
52
|
|
|
48
53
|
const BAD_ECHO_MESSAGE =
|
|
49
|
-
"Qoder returned an unparseable tool-call echo; no tools were emitted";
|
|
54
|
+
"Qoder returned an unparseable tool-call echo (request {requestId}); no tools were emitted. The upstream came back empty-handed — retry 🌙";
|
|
50
55
|
|
|
51
56
|
function badEchoError(): GatewayError {
|
|
52
57
|
return new GatewayError(
|
|
@@ -232,7 +237,9 @@ export function qoderMachineIdForAccount(accountId: string): string {
|
|
|
232
237
|
* directly as the COSY identity's oauth token; the user id and machine id
|
|
233
238
|
* come from the `auth_state` the login persisted. Fails closed with a
|
|
234
239
|
* reconnect prompt when the identity is absent (e.g. an account created
|
|
235
|
-
* before `auth_state` existed
|
|
240
|
+
* before `auth_state` existed, or an import that dropped the field) instead
|
|
241
|
+
* of signing with a guessed id. The message names the missing field so an
|
|
242
|
+
* operator can fix the export instead of re-logging blind.
|
|
236
243
|
*/
|
|
237
244
|
export function qoderOAuthAuth(
|
|
238
245
|
secret: string,
|
|
@@ -240,11 +247,11 @@ export function qoderOAuthAuth(
|
|
|
240
247
|
accountId: string | undefined,
|
|
241
248
|
): QoderAuth {
|
|
242
249
|
const state = parseQoderOAuthState(authState);
|
|
243
|
-
if (state === undefined
|
|
250
|
+
if (state === undefined) {
|
|
244
251
|
throw new GatewayError(
|
|
245
252
|
"authentication_failed",
|
|
246
253
|
401,
|
|
247
|
-
"Qoder OAuth account is missing its user identity; sign in again",
|
|
254
|
+
"Qoder OAuth account is missing its user identity (request {requestId}) (auth_state.userId/machineId absent or empty — re-login, or re-export with authState included); sign in again. Wrong key, right door — check the credential 🔑",
|
|
248
255
|
);
|
|
249
256
|
}
|
|
250
257
|
return {
|
|
@@ -385,14 +392,14 @@ async function exchangeQoderPat(
|
|
|
385
392
|
try {
|
|
386
393
|
responseBody = await response.json();
|
|
387
394
|
} catch {
|
|
388
|
-
throw new GatewayError("platform_unavailable", 502, "Qoder PAT exchange returned invalid JSON", {}, "upstream");
|
|
395
|
+
throw new GatewayError("platform_unavailable", 502, "Qoder PAT exchange returned invalid JSON (request {requestId}). The upstream came back empty-handed — retry 🌙", {}, "upstream");
|
|
389
396
|
}
|
|
390
397
|
if (typeof responseBody !== "object" || responseBody === null || Array.isArray(responseBody)) {
|
|
391
|
-
throw new GatewayError("platform_unavailable", 502, "Qoder PAT exchange returned unexpected response", {}, "upstream");
|
|
398
|
+
throw new GatewayError("platform_unavailable", 502, "Qoder PAT exchange returned unexpected response (request {requestId}). The upstream came back empty-handed — retry 🌙", {}, "upstream");
|
|
392
399
|
}
|
|
393
400
|
const rec = responseBody as Record<string, unknown>;
|
|
394
401
|
if (typeof rec["id"] !== "string" || typeof rec["securityOauthToken"] !== "string") {
|
|
395
|
-
throw new GatewayError("platform_unavailable", 502, "Qoder PAT exchange returned incomplete credentials", {}, "upstream");
|
|
402
|
+
throw new GatewayError("platform_unavailable", 502, "Qoder PAT exchange returned incomplete credentials (request {requestId}). The upstream came back empty-handed — retry 🌙", {}, "upstream");
|
|
396
403
|
}
|
|
397
404
|
return {
|
|
398
405
|
userId: rec["id"] as string,
|
|
@@ -479,7 +486,7 @@ function qoderEnvelopeToFrames(data: string): Array<{ event: string | null; data
|
|
|
479
486
|
throw new GatewayError(
|
|
480
487
|
classification.code,
|
|
481
488
|
classification.status,
|
|
482
|
-
detail.length > 0 ? detail : `Qoder stream envelope returned HTTP ${status}
|
|
489
|
+
detail.length > 0 ? `${detail} (request {requestId}). The upstream came back empty-handed — retry 🌙` : `Qoder stream envelope returned HTTP ${status} (request {requestId}). The upstream came back empty-handed — retry 🌙`,
|
|
483
490
|
{
|
|
484
491
|
providerId: QODER_PROVIDER_ID,
|
|
485
492
|
providerStatus: status,
|
|
@@ -496,8 +503,11 @@ function qoderEnvelopeToFrames(data: string): Array<{ event: string | null; data
|
|
|
496
503
|
}
|
|
497
504
|
const body = rec["body"];
|
|
498
505
|
if (body === "[DONE]") return [{ event: null, data: "[DONE]" }];
|
|
499
|
-
if (typeof body
|
|
500
|
-
|
|
506
|
+
if (typeof body === "string") return [{ event: null, data: body.replace(/\r?\n/g, "") }];
|
|
507
|
+
// Direct OpenAI chunk (IDE surface): no envelope body, but a `choices` array
|
|
508
|
+
// is present. Pass the raw data through so the chunk parser sees it.
|
|
509
|
+
if (Array.isArray(rec["choices"])) return [{ event: null, data }];
|
|
510
|
+
return [];
|
|
501
511
|
}
|
|
502
512
|
|
|
503
513
|
function qoderStableHash(prefix: string, ...parts: string[]): string {
|
|
@@ -538,6 +548,21 @@ function qoderStableRecordId(modelId: string, messages: CanonicalRequest["messag
|
|
|
538
548
|
return h.digest("hex").slice(0, 16);
|
|
539
549
|
}
|
|
540
550
|
|
|
551
|
+
/**
|
|
552
|
+
* The widest selectable context window for one model key.
|
|
553
|
+
*
|
|
554
|
+
* The live list's `context_config` is the authority when discovery has run:
|
|
555
|
+
* it names every window the account can switch to, and the widest one is what
|
|
556
|
+
* a dispatch should send (`model_config.max_input_tokens`) so a conversation is
|
|
557
|
+
* never capped at the *currently active* window (often 180k) when the account
|
|
558
|
+
* can use 1M. Falls back to the static roster's stated window otherwise.
|
|
559
|
+
*/
|
|
560
|
+
function qoderMaxContextFor(modelId: string, modelConfig: QoderModelConfig, realm: QoderRealm): number {
|
|
561
|
+
const discovered = qoderDiscoveredMaxContexts(realm).get(modelId);
|
|
562
|
+
if (discovered !== undefined) return discovered;
|
|
563
|
+
return modelConfig.max_input_tokens;
|
|
564
|
+
}
|
|
565
|
+
|
|
541
566
|
function qoderRequestedMaxTokens(request: CanonicalRequest, modelConfig: QoderModelConfig): number {
|
|
542
567
|
/**
|
|
543
568
|
* The ceiling is the roster's stated one. A fixed 32k fallback would clamp
|
|
@@ -925,7 +950,11 @@ export function buildQoderRequest(
|
|
|
925
950
|
url: "",
|
|
926
951
|
is_vl: modelConfig.is_vl,
|
|
927
952
|
is_reasoning: modelConfig.is_reasoning,
|
|
928
|
-
|
|
953
|
+
// Send the widest selectable window from the live list's `context_config`
|
|
954
|
+
// (falling back to the static roster's stated window). Without this, the
|
|
955
|
+
// request would declare the *currently active* window — often 180k — even
|
|
956
|
+
// when the account can use 1M, capping the conversation the upstream sees.
|
|
957
|
+
max_input_tokens: qoderMaxContextFor(modelId, modelConfig, realm),
|
|
929
958
|
// Mirrors the roster's stated ceiling. A fixed 32k fallback would be sent
|
|
930
959
|
// for every model alike, so the request would declare a cap the catalog
|
|
931
960
|
// does not publish.
|
|
@@ -949,9 +978,89 @@ export function buildQoderRequest(
|
|
|
949
978
|
};
|
|
950
979
|
}
|
|
951
980
|
|
|
981
|
+
/**
|
|
982
|
+
* Builds the modern model-server (`/model/v1/chat/completions`) request body.
|
|
983
|
+
*
|
|
984
|
+
* It reuses the message translation from `buildQoderRequest` (tool turns,
|
|
985
|
+
* reasoning echo, image parts) and wraps the result in the OpenAI-shaped body
|
|
986
|
+
* the IDE surface expects: top-level `model`, `messages`, `stream_options`,
|
|
987
|
+
* and a `metadata.context`/`metadata.business` block. `model` is the roster
|
|
988
|
+
* key after virtual-name resolution, so the upstream never sees a virtual id
|
|
989
|
+
* (which it answers by falling back to `auto`).
|
|
990
|
+
*/
|
|
991
|
+
export function buildQoderIdeRequest(
|
|
992
|
+
modelId: string,
|
|
993
|
+
request: CanonicalRequest,
|
|
994
|
+
modelConfig: QoderModelConfig,
|
|
995
|
+
auth: QoderAuth,
|
|
996
|
+
realm: QoderRealm = "intl",
|
|
997
|
+
version = getQoderVersion(),
|
|
998
|
+
): Record<string, unknown> {
|
|
999
|
+
const legacy = buildQoderRequest(modelId, request, modelConfig, auth, realm);
|
|
1000
|
+
const messages = legacy.messages as Array<Record<string, unknown>>;
|
|
1001
|
+
return {
|
|
1002
|
+
model: modelId,
|
|
1003
|
+
messages,
|
|
1004
|
+
stream: true,
|
|
1005
|
+
stream_options: { include_usage: true },
|
|
1006
|
+
metadata: {
|
|
1007
|
+
context: {
|
|
1008
|
+
request_id: legacy.request_id,
|
|
1009
|
+
request_set_id: legacy.request_set_id,
|
|
1010
|
+
session_id: legacy.session_id,
|
|
1011
|
+
source_session_id: "",
|
|
1012
|
+
task_id: "common",
|
|
1013
|
+
client_type: "5",
|
|
1014
|
+
},
|
|
1015
|
+
business: {
|
|
1016
|
+
business_product: "cli",
|
|
1017
|
+
business_type: "agent",
|
|
1018
|
+
business_version: version,
|
|
1019
|
+
scene: "assistant",
|
|
1020
|
+
},
|
|
1021
|
+
},
|
|
1022
|
+
max_tokens: (legacy.max_tokens as number) ?? 65_536,
|
|
1023
|
+
};
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
/** Identity headers for the modern model-server surface (plain bearer). */
|
|
1027
|
+
function qoderIdeHeaders(auth: QoderAuth, version: string): Record<string, string> {
|
|
1028
|
+
return {
|
|
1029
|
+
Authorization: `Bearer ${auth.securityOauthToken}`,
|
|
1030
|
+
"Content-Type": "application/json",
|
|
1031
|
+
Accept: "text/event-stream",
|
|
1032
|
+
"X-Request-ID": randomUUID(),
|
|
1033
|
+
"X-Session-ID": randomUUID(),
|
|
1034
|
+
"Cosy-Version": version,
|
|
1035
|
+
"Cosy-ClientType": "5",
|
|
1036
|
+
"Cosy-MachineOS": qoderMachineOs(),
|
|
1037
|
+
"Cosy-MachineHostname": "DESKTOP-QODER",
|
|
1038
|
+
"Cosy-MachineId": auth.machineId,
|
|
1039
|
+
"Cosy-MachineToken": auth.machineId,
|
|
1040
|
+
"Cosy-MachineType": "5",
|
|
1041
|
+
};
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
async function callQoderIde(
|
|
1045
|
+
url: string,
|
|
1046
|
+
body: Record<string, unknown>,
|
|
1047
|
+
auth: QoderAuth,
|
|
1048
|
+
signal: AbortSignal,
|
|
1049
|
+
fetcher: QoderFetcher,
|
|
1050
|
+
version = getQoderVersion(),
|
|
1051
|
+
): Promise<Response> {
|
|
1052
|
+
return fetcher(url, {
|
|
1053
|
+
method: "POST",
|
|
1054
|
+
headers: qoderIdeHeaders(auth, version),
|
|
1055
|
+
body: JSON.stringify(body),
|
|
1056
|
+
signal,
|
|
1057
|
+
});
|
|
1058
|
+
}
|
|
1059
|
+
|
|
952
1060
|
// Stream transformation — decode SSE, unwrap COSY envelope, map OpenAI chat chunks to CanonicalEvent
|
|
953
1061
|
|
|
954
1062
|
function mapQoderFinishReason(raw: unknown): CanonicalStopReason | undefined {
|
|
1063
|
+
if (raw === "stop") return "stop";
|
|
955
1064
|
if (raw === "length") return "length";
|
|
956
1065
|
if (raw === "tool_calls") return "tool_use";
|
|
957
1066
|
if (raw === "content_filter") return "content_filter";
|
|
@@ -1000,7 +1109,39 @@ export async function* qoderBodyToCanonicalEvents(
|
|
|
1000
1109
|
} catch {
|
|
1001
1110
|
continue;
|
|
1002
1111
|
}
|
|
1112
|
+
// IDE surface error event: `{"code":"invalid_model_error","message":"..."}`
|
|
1113
|
+
// carries no choices. Surface it as an upstream failure instead of an
|
|
1114
|
+
// empty successful stream.
|
|
1115
|
+
if (json["error"] !== undefined && json["choices"] === undefined) {
|
|
1116
|
+
const error = json["error"] as Record<string, unknown> | string;
|
|
1117
|
+
const message =
|
|
1118
|
+
typeof error === "string"
|
|
1119
|
+
? error
|
|
1120
|
+
: typeof error?.["message"] === "string"
|
|
1121
|
+
? (error["message"] as string)
|
|
1122
|
+
: typeof json["message"] === "string"
|
|
1123
|
+
? (json["message"] as string)
|
|
1124
|
+
: "Qoder model server error";
|
|
1125
|
+
const code = typeof json["code"] === "string" ? (json["code"] as string) : undefined;
|
|
1126
|
+
throw new GatewayError(
|
|
1127
|
+
code === "invalid_model_error" ? "model_not_found" : "platform_unavailable",
|
|
1128
|
+
code === "invalid_model_error" ? 404 : 502,
|
|
1129
|
+
`${message} (request {requestId}). The upstream came back empty-handed — retry 🌙`,
|
|
1130
|
+
{ providerId: QODER_PROVIDER_ID, ...(code === undefined ? {} : { providerCode: code }) },
|
|
1131
|
+
"upstream",
|
|
1132
|
+
);
|
|
1133
|
+
}
|
|
1003
1134
|
if (json["usage"] && typeof json["usage"] === "object") rawUsage = json["usage"] as Record<string, unknown>;
|
|
1135
|
+
// IDE surface terminal event: `choices:[]` + `raw_usage`. It carries the
|
|
1136
|
+
// final usage and confirms the turn ended, so record it and synthesize a
|
|
1137
|
+
// stop reason — the terminal event has no `choices[].finish_reason`.
|
|
1138
|
+
if (json["raw_usage"] && typeof json["raw_usage"] === "object") {
|
|
1139
|
+
const rawUsageObj = json["raw_usage"] as Record<string, unknown>;
|
|
1140
|
+
rawUsage = (rawUsageObj["data"] as Record<string, unknown> | undefined) ?? rawUsageObj;
|
|
1141
|
+
if (json["choices"] === undefined || (json["choices"] as unknown[]).length === 0) {
|
|
1142
|
+
finishReason = finishReason ?? "stop";
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1004
1145
|
const choices = (json["choices"] as Array<Record<string, unknown>>) ?? [];
|
|
1005
1146
|
for (const choice of choices) {
|
|
1006
1147
|
const delta = choice["delta"] as Record<string, unknown> | undefined;
|
|
@@ -1126,12 +1267,16 @@ export async function* qoderBodyToCanonicalEvents(
|
|
|
1126
1267
|
}
|
|
1127
1268
|
}
|
|
1128
1269
|
const fr = choice["finish_reason"];
|
|
1129
|
-
|
|
1270
|
+
// The IDE surface sends an empty string (`""`) for the per-delta finish
|
|
1271
|
+
// reason, which is not a real stop signal — only a value that maps to a
|
|
1272
|
+
// canonical stop reason (or the terminal `[DONE]` frame) ends the turn.
|
|
1273
|
+
if (fr !== null && fr !== undefined && fr !== "") finishReason = fr;
|
|
1130
1274
|
// Some providers send usage in choices[0].usage or top-level
|
|
1131
1275
|
if (choice["usage"] && typeof choice["usage"] === "object") rawUsage = choice["usage"] as Record<string, unknown>;
|
|
1132
1276
|
}
|
|
1133
|
-
// Some Qoder chunks carry finish_reason at top level
|
|
1134
|
-
|
|
1277
|
+
// Some Qoder chunks carry finish_reason at top level (empty string means
|
|
1278
|
+
// no stop signal, same as the per-delta field).
|
|
1279
|
+
if (json["finish_reason"] !== undefined && json["finish_reason"] !== "") finishReason = json["finish_reason"];
|
|
1135
1280
|
}
|
|
1136
1281
|
}
|
|
1137
1282
|
// End of stream: recover a held-back marker+JSON echo. A valid echo becomes
|
|
@@ -1166,7 +1311,12 @@ export async function* qoderBodyToCanonicalEvents(
|
|
|
1166
1311
|
}
|
|
1167
1312
|
}
|
|
1168
1313
|
}
|
|
1169
|
-
//
|
|
1314
|
+
// A `[DONE]` frame from the IDE surface may be the only terminal signal (the
|
|
1315
|
+
// per-delta `finish_reason` is empty and `raw_usage` is not always sent), so
|
|
1316
|
+
// a clean end-of-stream with no finish reason is a normal stop, not a
|
|
1317
|
+
// truncation. If no terminal arrived at all, still synthesize one (prevents
|
|
1318
|
+
// truncation error).
|
|
1319
|
+
if (hasTerminal && finishReason === undefined) finishReason = "stop";
|
|
1170
1320
|
const stopReason = mapQoderFinishReason(finishReason);
|
|
1171
1321
|
const _providerStop = typeof finishReason === "string" ? finishReason : undefined;
|
|
1172
1322
|
void hasTerminal;
|
|
@@ -1202,16 +1352,28 @@ interface QoderAdapterOptions {
|
|
|
1202
1352
|
readonly realm?: QoderRealm;
|
|
1203
1353
|
/** Bundled provider id, so the adapter's own id matches the route's. */
|
|
1204
1354
|
readonly providerId?: string;
|
|
1355
|
+
/**
|
|
1356
|
+
* Which chat surface to dispatch on.
|
|
1357
|
+
*
|
|
1358
|
+
* `ide` (default) uses the modern model-server surface
|
|
1359
|
+
* (`/model/v1/chat/completions`, plain bearer, OpenAI-shaped chunks): it
|
|
1360
|
+
* streams immediately. The legacy `cosy` surface (`/algo/.../agent_chat_generation`)
|
|
1361
|
+
* answers `10605 isQueued` and parks requests in a server queue, so it is a
|
|
1362
|
+
* fallback, not a default.
|
|
1363
|
+
*/
|
|
1364
|
+
readonly surface?: "ide" | "cosy";
|
|
1205
1365
|
}
|
|
1206
1366
|
|
|
1207
1367
|
class QoderAdapter implements ProviderAdapter {
|
|
1208
1368
|
readonly provider_id: ProviderId;
|
|
1209
1369
|
private readonly realm: QoderRealm;
|
|
1370
|
+
private readonly surface: "ide" | "cosy";
|
|
1210
1371
|
private readonly fetchFn: typeof fetch;
|
|
1211
1372
|
|
|
1212
1373
|
constructor(options: QoderAdapterOptions = {}) {
|
|
1213
1374
|
this.provider_id = (options.providerId ?? QODER_PROVIDER_ID) as ProviderId;
|
|
1214
1375
|
this.realm = options.realm ?? "intl";
|
|
1376
|
+
this.surface = options.surface ?? "ide";
|
|
1215
1377
|
this.fetchFn = options.fetch ?? globalThis.fetch;
|
|
1216
1378
|
}
|
|
1217
1379
|
|
|
@@ -1234,7 +1396,7 @@ class QoderAdapter implements ProviderAdapter {
|
|
|
1234
1396
|
context: ProviderDispatchContext,
|
|
1235
1397
|
): AsyncIterable<CanonicalEvent> {
|
|
1236
1398
|
if (candidate.wire_family !== "chat") {
|
|
1237
|
-
|
|
1399
|
+
assertWireFamily(candidate, "chat", "qoder");
|
|
1238
1400
|
}
|
|
1239
1401
|
const secret = readCredentialSecret(context.credential, "Qoder credential is required");
|
|
1240
1402
|
|
|
@@ -1251,7 +1413,7 @@ class QoderAdapter implements ProviderAdapter {
|
|
|
1251
1413
|
throw new GatewayError(
|
|
1252
1414
|
"model_not_found",
|
|
1253
1415
|
404,
|
|
1254
|
-
`Qoder virtual model ${requestedModelId} resolves to a roster key this realm does not serve
|
|
1416
|
+
`Qoder virtual model ${requestedModelId} resolves to a roster key this realm does not serve (request {requestId}). Try another model — this one's off the menu 📚`,
|
|
1255
1417
|
{ virtual_model: requestedModelId, target: qoderDispatchModelId(requestedModelId) },
|
|
1256
1418
|
"upstream",
|
|
1257
1419
|
);
|
|
@@ -1281,7 +1443,6 @@ class QoderAdapter implements ProviderAdapter {
|
|
|
1281
1443
|
const auth = isQoderPat(secret)
|
|
1282
1444
|
? await exchangeQoderPat(secret, lifecycle.signal, fetcher, version, this.realm)
|
|
1283
1445
|
: qoderOAuthAuth(secret, context.credential.auth_state, context.credential.account_id);
|
|
1284
|
-
const qoderBody = buildQoderRequest(modelId, request, modelConfig, auth, this.realm);
|
|
1285
1446
|
// A full URL from the route wins, but the fallback is this realm's
|
|
1286
1447
|
// gateway — not the intl host hardcoded into `MODERN_PROFILE`. Dispatching
|
|
1287
1448
|
// a CN credential to `*.qoder.sh` is refused outright. The host order
|
|
@@ -1290,11 +1451,18 @@ class QoderAdapter implements ProviderAdapter {
|
|
|
1290
1451
|
const url =
|
|
1291
1452
|
candidate.endpoint_path && candidate.endpoint_path.startsWith("http")
|
|
1292
1453
|
? candidate.endpoint_path
|
|
1293
|
-
:
|
|
1454
|
+
: this.surface === "ide"
|
|
1455
|
+
? qoderIdeChatUrl(this.realm)
|
|
1456
|
+
: `${qoderInferenceCandidates(this.realm, auth.securityOauthToken)[0] ?? ""}${QODER_CHAT_PATH}`;
|
|
1294
1457
|
|
|
1295
|
-
|
|
1458
|
+
// The modern IDE surface streams immediately and needs no COSY signing;
|
|
1459
|
+
// the legacy COSY surface is kept as an explicit fallback.
|
|
1460
|
+
const response =
|
|
1461
|
+
this.surface === "ide"
|
|
1462
|
+
? await callQoderIde(url, buildQoderIdeRequest(modelId, request, modelConfig, auth, this.realm, version), auth, lifecycle.signal, fetcher, version)
|
|
1463
|
+
: await callQoder(url, buildQoderRequest(modelId, request, modelConfig, auth, this.realm), modelId, auth, lifecycle.signal, fetcher, version);
|
|
1296
1464
|
if (!response.ok) throw await mapUpstreamHttpError(response, "qoder");
|
|
1297
|
-
if (!response.body) throw new GatewayError("platform_unavailable", 502, "Qoder returned empty body", {}, "upstream");
|
|
1465
|
+
if (!response.body) throw new GatewayError("platform_unavailable", 502, "Qoder returned empty body (request {requestId}). The upstream came back empty-handed — retry 🌙", {}, "upstream");
|
|
1298
1466
|
// Headers arrived: the pre-stream deadline has served its purpose.
|
|
1299
1467
|
// From here the gateway stall/first-chunk watchdog (propagated via
|
|
1300
1468
|
// context.abort_signal) governs the body. Keeping this timer armed
|
|
@@ -71,11 +71,11 @@ export const TOKENHARBOR_FALLBACK_MODELS: readonly ModelDefinition[] = [
|
|
|
71
71
|
function tokenharborPrePayload(payload: Record<string, unknown>): void {
|
|
72
72
|
const model = payload["model"];
|
|
73
73
|
if (typeof model !== "string" || model.length === 0) {
|
|
74
|
-
throw new GatewayError("invalid_request", 400, "TokenHarbor model is required");
|
|
74
|
+
throw new GatewayError("invalid_request", 400, "TokenHarbor model is required (request {requestId}). Check the shape of what you sent — then resend 🪞");
|
|
75
75
|
}
|
|
76
76
|
// Strip accidental query-string smuggling (mirrors the factory's own guard)
|
|
77
77
|
if (model.includes("?")) {
|
|
78
|
-
throw new GatewayError("invalid_request", 400, "TokenHarbor model must not contain query string");
|
|
78
|
+
throw new GatewayError("invalid_request", 400, "TokenHarbor model must not contain query string (request {requestId}). Check the shape of what you sent — then resend 🪞");
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
81
|
|
|
@@ -10,6 +10,7 @@ import type { UsageRecord } from "../../../protocol/canonical-model";
|
|
|
10
10
|
import { usageFromProvider } from "../../provider-usage";
|
|
11
11
|
import {
|
|
12
12
|
readCredentialSecret,
|
|
13
|
+
assertWireFamily,
|
|
13
14
|
type ProviderAdapter,
|
|
14
15
|
type ProviderDispatchContext,
|
|
15
16
|
type ProviderDispatchTarget,
|
|
@@ -67,7 +68,7 @@ const STOP_REASONS: Readonly<Record<string, CanonicalStopReason>> = {
|
|
|
67
68
|
function traeAuth(secret: string, accountId: string | undefined): TraeAuth {
|
|
68
69
|
const token = extractToken(secret);
|
|
69
70
|
if (token === undefined) {
|
|
70
|
-
throw new GatewayError("authentication_failed", 401, "Trae credential has no access token", {}, "upstream");
|
|
71
|
+
throw new GatewayError("authentication_failed", 401, "Trae credential has no access token (request {requestId}). Wrong key, right door — check the credential 🔑", {}, "upstream");
|
|
71
72
|
}
|
|
72
73
|
return { token, uid: accountId ?? "" };
|
|
73
74
|
}
|
|
@@ -116,7 +117,7 @@ function throwTraeUpstreamError(codeRaw: unknown, messageRaw: unknown): never {
|
|
|
116
117
|
throw new GatewayError(
|
|
117
118
|
"authentication_failed",
|
|
118
119
|
401,
|
|
119
|
-
`Trae could not authenticate the account (upstream code 1001): ${message}
|
|
120
|
+
`Trae could not authenticate the account (upstream code 1001): ${message} (request {requestId}). Wrong key, right door — check the credential 🔑`,
|
|
120
121
|
{ providerCode: "1001", providerStatus: 1001, credentialEvidence: true },
|
|
121
122
|
"upstream",
|
|
122
123
|
);
|
|
@@ -127,7 +128,7 @@ function throwTraeUpstreamError(codeRaw: unknown, messageRaw: unknown): never {
|
|
|
127
128
|
throw new GatewayError(
|
|
128
129
|
"quota_exceeded",
|
|
129
130
|
429,
|
|
130
|
-
`Trae quota exceeded (upstream code ${code ?? "unknown"}): ${message}
|
|
131
|
+
`Trae quota exceeded (upstream code ${code ?? "unknown"}): ${message} (request {requestId}). You've maxed this window — pace yourself ⏳`,
|
|
131
132
|
{ providerCode: code ?? "4008", providerStatus: code === "4008" ? 4008 : 429 },
|
|
132
133
|
"upstream",
|
|
133
134
|
);
|
|
@@ -152,11 +153,7 @@ class TraeAdapter implements ProviderAdapter {
|
|
|
152
153
|
context: ProviderDispatchContext,
|
|
153
154
|
): AsyncIterable<CanonicalEvent> {
|
|
154
155
|
if (candidate.wire_family !== "chat") {
|
|
155
|
-
|
|
156
|
-
"capability_unsupported",
|
|
157
|
-
400,
|
|
158
|
-
`trae supports chat only, got ${candidate.wire_family}`,
|
|
159
|
-
);
|
|
156
|
+
assertWireFamily(candidate, "chat", "trae");
|
|
160
157
|
}
|
|
161
158
|
const secret = readCredentialSecret(context.credential, "Trae credential is required");
|
|
162
159
|
const modelId = candidate.model_id || request.model;
|
|
@@ -214,7 +211,7 @@ class TraeAdapter implements ProviderAdapter {
|
|
|
214
211
|
});
|
|
215
212
|
if (!response.ok) throw await mapUpstreamHttpError(response, this.provider_id);
|
|
216
213
|
if (!response.body) {
|
|
217
|
-
throw new GatewayError("platform_unavailable", 502, "Trae returned empty body", {}, "upstream");
|
|
214
|
+
throw new GatewayError("platform_unavailable", 502, "Trae returned empty body (request {requestId}). The upstream came back empty-handed — retry 🌙", {}, "upstream");
|
|
218
215
|
}
|
|
219
216
|
// Headers arrived, so the pre-stream deadline has served its purpose;
|
|
220
217
|
// from here the gateway's own stall watchdog governs the body.
|
|
@@ -78,7 +78,7 @@ function unwrapEnvelope(body: unknown, operation: string): unknown {
|
|
|
78
78
|
// codebase is 401. A 400 told the client to fix its body when the real
|
|
79
79
|
// action is to fix the credential — and HTTP itself reserves 401 for
|
|
80
80
|
// exactly this, so a standard client reads the class from the status.
|
|
81
|
-
throw new GatewayError("authentication_failed", 401, `Z.AI ${message}
|
|
81
|
+
throw new GatewayError("authentication_failed", 401, `Z.AI ${message} (request {requestId}). Wrong key, right door — check the credential 🔑`, { providerId: "zcode" });
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
84
|
return root.data ?? body;
|
|
@@ -251,15 +251,13 @@ export interface AccountErrorClassification {
|
|
|
251
251
|
/**
|
|
252
252
|
* True when this write changed a field the routing snapshot actually reads.
|
|
253
253
|
*
|
|
254
|
-
*
|
|
255
|
-
*
|
|
256
|
-
*
|
|
257
|
-
*
|
|
258
|
-
* model-scoped cooldown — is invisible to routing, and invalidating for it
|
|
259
|
-
* forced the next request to rebuild an 11-SELECT snapshot for no reason.
|
|
254
|
+
* Routing reads account-wide `status`/`cooldownUntil` and the per-model
|
|
255
|
+
* `modelCooldowns` map (`route-catalog.ts`). Writes to any of those must drop
|
|
256
|
+
* the snapshot; failure streaks and diagnostic text remain invisible to
|
|
257
|
+
* routing.
|
|
260
258
|
*
|
|
261
|
-
*
|
|
262
|
-
*
|
|
259
|
+
* Only a changed deadline is routing-visible. Re-reporting the same active
|
|
260
|
+
* model cooldown must not rebuild the snapshot again.
|
|
263
261
|
*/
|
|
264
262
|
readonly routesVisible: boolean;
|
|
265
263
|
/**
|
|
@@ -147,13 +147,13 @@ export async function recordAccountFailure(
|
|
|
147
147
|
// Monotonic: never move an in-force window earlier (see `latestDeadlineMs`).
|
|
148
148
|
// A provider-stated deadline wins outright because it is already the later
|
|
149
149
|
// of the two whenever the provider really parked the pair longer.
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
).toISOString();
|
|
150
|
+
const previousDeadline = existing[modelId] === undefined ? undefined : Date.parse(existing[modelId]);
|
|
151
|
+
const nextDeadline = latestDeadlineMs(
|
|
152
|
+
effective.retryAt?.getTime(),
|
|
153
|
+
Date.now() + (unsupportedCooldownMs ?? escalatedCooldownMs),
|
|
154
|
+
previousDeadline,
|
|
155
|
+
);
|
|
156
|
+
const nextUntil = new Date(nextDeadline).toISOString();
|
|
157
157
|
const previousUntil = existing[modelId];
|
|
158
158
|
// A re-statement is not an event: what the audit row reports is the
|
|
159
159
|
// *entry* into a cooling episode, and the deadline itself is not a
|
|
@@ -213,10 +213,10 @@ export async function recordAccountFailure(
|
|
|
213
213
|
createdAt: now,
|
|
214
214
|
});
|
|
215
215
|
}
|
|
216
|
-
//
|
|
217
|
-
//
|
|
218
|
-
//
|
|
219
|
-
return { ...classification, routesVisible:
|
|
216
|
+
// The route catalog projects modelCooldowns and stamps this candidate as
|
|
217
|
+
// model_cooldown. Invalidate when its deadline changes, or the cached
|
|
218
|
+
// snapshot keeps routing this pair until some unrelated write rebuilds it.
|
|
219
|
+
return { ...classification, routesVisible: previousDeadline !== nextDeadline };
|
|
220
220
|
}
|
|
221
221
|
|
|
222
222
|
await client
|
|
@@ -550,11 +550,9 @@ export async function reportAttemptOutcome(
|
|
|
550
550
|
...report.evidence,
|
|
551
551
|
...(report.modelId === undefined ? {} : { modelId: report.modelId }),
|
|
552
552
|
}).catch(() => null);
|
|
553
|
-
// Gated on `routesVisible`, not on `mutatesAccount`: a
|
|
554
|
-
//
|
|
555
|
-
//
|
|
556
|
-
// on the next request, and under a failure wave each rebuild fed the next
|
|
557
|
-
// invalidation.
|
|
553
|
+
// Gated on `routesVisible`, not on `mutatesAccount`: a model cooldown changes
|
|
554
|
+
// routing, while a repeated report with the same deadline or a streak-only
|
|
555
|
+
// write does not.
|
|
558
556
|
if (classification?.routesVisible) await report.snapshotService?.invalidate();
|
|
559
557
|
}
|
|
560
558
|
|