cartethyia 2.7.9 → 2.8.1
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 +6 -5
- package/dist/dashboard/assets/{BannedPage-BxoG1MrL.js → BannedPage-Dq530awD.js} +1 -1
- package/dist/dashboard/assets/BannedPage-Dq530awD.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolDetailPage-ixwgKk53.js → CliToolDetailPage-DZzaS-7S.js} +1 -1
- package/dist/dashboard/assets/CliToolDetailPage-DZzaS-7S.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolsPage-BmttNV3U.js → CliToolsPage-DaZZ3cqP.js} +1 -1
- package/dist/dashboard/assets/CliToolsPage-DaZZ3cqP.js.gz +0 -0
- package/dist/dashboard/assets/{CombosPage-BE3qwwfC.js → CombosPage-BlC_xdkX.js} +1 -1
- package/dist/dashboard/assets/CombosPage-BlC_xdkX.js.gz +0 -0
- package/dist/dashboard/assets/{ConsoleLogPage-CRZdmMIX.js → ConsoleLogPage-D-rqeVtG.js} +1 -1
- package/dist/dashboard/assets/ConsoleLogPage-D-rqeVtG.js.gz +0 -0
- package/dist/dashboard/assets/{CustomizationPage-Dv_FJ6Er.js → CustomizationPage-7xT5yg2E.js} +1 -1
- package/dist/dashboard/assets/CustomizationPage-7xT5yg2E.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-Clq8E9cW.js +1 -0
- package/dist/dashboard/assets/HealthEventsModal-Clq8E9cW.js.gz +0 -0
- package/dist/dashboard/assets/{InjectSystemPromptPanel-BOoO8rpz.js → InjectSystemPromptPanel-6Teoq9Se.js} +2 -2
- package/dist/dashboard/assets/InjectSystemPromptPanel-6Teoq9Se.js.gz +0 -0
- package/dist/dashboard/assets/{LoginPage-ChJuNNVN.js → LoginPage-CZuI0MAR.js} +1 -1
- package/dist/dashboard/assets/LoginPage-CZuI0MAR.js.gz +0 -0
- package/dist/dashboard/assets/{ModelPicker-BZAJtPg1.js → ModelPicker-C1hNTlkr.js} +1 -1
- package/dist/dashboard/assets/ModelPicker-C1hNTlkr.js.gz +0 -0
- package/dist/dashboard/assets/{OverviewPage-BE40s3u5.js → OverviewPage-0yfhSI1C.js} +1 -1
- package/dist/dashboard/assets/OverviewPage-0yfhSI1C.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-xqTxdQ4c.js +8 -0
- package/dist/dashboard/assets/ProviderDetailPage-xqTxdQ4c.js.gz +0 -0
- package/dist/dashboard/assets/{ProviderIcon-CQ6bPUf_.js → ProviderIcon-Ddhm1InL.js} +1 -1
- package/dist/dashboard/assets/ProviderIcon-Ddhm1InL.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-C3fYuu6i.js +1 -0
- package/dist/dashboard/assets/ProvidersPage-C3fYuu6i.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-CHtJtZ3u.js +9 -0
- package/dist/dashboard/assets/ProxyPage-CHtJtZ3u.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-D1RV_2BX.js +1 -0
- package/dist/dashboard/assets/QuotaPage-D1RV_2BX.js.gz +0 -0
- package/dist/dashboard/assets/{SettingsPage-C5HPWqEv.js → SettingsPage-BzZAvYSX.js} +1 -1
- package/dist/dashboard/assets/SettingsPage-BzZAvYSX.js.gz +0 -0
- package/dist/dashboard/assets/{SetupPage-CFNcATJJ.js → SetupPage--s-EfEIL.js} +1 -1
- package/dist/dashboard/assets/SetupPage--s-EfEIL.js.gz +0 -0
- package/dist/dashboard/assets/{StudioPage-YoD4tY5l.js → StudioPage-BjXFGSK4.js} +1 -1
- package/dist/dashboard/assets/StudioPage-BjXFGSK4.js.gz +0 -0
- package/dist/dashboard/assets/{UsagePage-DxjaAhb_.js → UsagePage-CP8F8BN7.js} +1 -1
- package/dist/dashboard/assets/UsagePage-CP8F8BN7.js.gz +0 -0
- package/dist/dashboard/assets/{api-keys-BTlkq_Uk.js → api-keys-2hc12Uz2.js} +1 -1
- package/dist/dashboard/assets/api-keys-2hc12Uz2.js.gz +0 -0
- package/dist/dashboard/assets/app-DGXbNfAR.css +2 -0
- package/dist/dashboard/assets/app-DGXbNfAR.css.gz +0 -0
- package/dist/dashboard/assets/assert-BMcj1xZE.js +1 -0
- package/dist/dashboard/assets/assert-BMcj1xZE.js.gz +0 -0
- package/dist/dashboard/assets/{badge-DdbT_7Am.js → badge-DkRUuVfz.js} +1 -1
- package/dist/dashboard/assets/badge-DkRUuVfz.js.gz +0 -0
- package/dist/dashboard/assets/{banner-CsGN-nbm.js → banner-CDjW841Q.js} +1 -1
- package/dist/dashboard/assets/banner-CDjW841Q.js.gz +0 -0
- package/dist/dashboard/assets/{card-mgZ_TLLZ.js → card-Bto5UlL9.js} +1 -1
- package/dist/dashboard/assets/card-Bto5UlL9.js.gz +0 -0
- package/dist/dashboard/assets/{cli-tools-A1-v8-03.js → cli-tools-DaH6g9hL.js} +1 -1
- package/dist/dashboard/assets/cli-tools-DaH6g9hL.js.gz +0 -0
- package/dist/dashboard/assets/{clipboard-button-CJ4Zty89.js → clipboard-button-BYKBCzjk.js} +1 -1
- package/dist/dashboard/assets/clipboard-button-BYKBCzjk.js.gz +0 -0
- package/dist/dashboard/assets/{dialog-B5L2jPQ_.js → dialog-DL0Zftwv.js} +1 -1
- package/dist/dashboard/assets/dialog-DL0Zftwv.js.gz +0 -0
- package/dist/dashboard/assets/{drawer-DkjUouAj.js → drawer-Dpx_UXaS.js} +1 -1
- package/dist/dashboard/assets/drawer-Dpx_UXaS.js.gz +0 -0
- package/dist/dashboard/assets/{entry-B-LVLV6o.js → entry-BV7-1NoM.js} +1 -1
- package/dist/dashboard/assets/entry-BV7-1NoM.js.gz +0 -0
- package/dist/dashboard/assets/{entry-CAhdYl3B.js → entry-CGvdNU6r.js} +1 -1
- package/dist/dashboard/assets/entry-CGvdNU6r.js.gz +0 -0
- package/dist/dashboard/assets/entry-DScKYvZj.js +4 -0
- package/dist/dashboard/assets/entry-DScKYvZj.js.gz +0 -0
- package/dist/dashboard/assets/{formatters-CrsdLVsn.js → formatters-BS3DZlZj.js} +1 -1
- package/dist/dashboard/assets/formatters-BS3DZlZj.js.gz +0 -0
- package/dist/dashboard/assets/{index-BU64Lqvp.js → index-SQnvvTWX.js} +2 -2
- package/dist/dashboard/assets/index-SQnvvTWX.js.gz +0 -0
- package/dist/dashboard/assets/{inline-B2aDLyyg.js → inline-Dl-cuLK0.js} +1 -1
- package/dist/dashboard/assets/inline-Dl-cuLK0.js.gz +0 -0
- package/dist/dashboard/assets/{input-BCVkdR9d.js → input-Bfx0EzRc.js} +1 -1
- package/dist/dashboard/assets/input-Bfx0EzRc.js.gz +0 -0
- package/dist/dashboard/assets/{layout-DNcAdMK-.js → layout-BxhbQ_JS.js} +1 -1
- package/dist/dashboard/assets/layout-BxhbQ_JS.js.gz +0 -0
- package/dist/dashboard/assets/{list-row-DgrIH0hw.js → list-row-D49Y4ptn.js} +1 -1
- package/dist/dashboard/assets/list-row-D49Y4ptn.js.gz +0 -0
- package/dist/dashboard/assets/{live-Cae01iu8.js → live-CbVTdX9V.js} +1 -1
- package/dist/dashboard/assets/live-CbVTdX9V.js.gz +0 -0
- package/dist/dashboard/assets/{network-CRib7H_2.js → network-JGvDveof.js} +1 -1
- package/dist/dashboard/assets/network-JGvDveof.js.gz +0 -0
- package/dist/dashboard/assets/{numbers-BKQE8Xpz.js → numbers-Dw6kiew-.js} +1 -1
- package/dist/dashboard/assets/numbers-Dw6kiew-.js.gz +0 -0
- package/dist/dashboard/assets/{pagination-ecPvG_8g.js → pagination-iUoI8GeE.js} +1 -1
- package/dist/dashboard/assets/pagination-iUoI8GeE.js.gz +0 -0
- package/dist/dashboard/assets/providers/sensenova-intl.png +0 -0
- package/dist/dashboard/assets/providers/sensenova.png +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/quota-DiPD6UKH.js +1 -0
- package/dist/dashboard/assets/quota-DiPD6UKH.js.gz +0 -0
- package/dist/dashboard/assets/{routing-Ckm1UXuC.js → routing-8jZr-3WE.js} +1 -1
- package/dist/dashboard/assets/routing-8jZr-3WE.js.gz +0 -0
- package/dist/dashboard/assets/{select-ChoFWyKz.js → select-oO1FxMVb.js} +2 -2
- package/dist/dashboard/assets/select-oO1FxMVb.js.gz +0 -0
- package/dist/dashboard/assets/{settings-CKjFPlKW.js → settings-CLr3kdIx.js} +1 -1
- package/dist/dashboard/assets/settings-CLr3kdIx.js.gz +0 -0
- package/dist/dashboard/assets/{stack-DWCwU3fh.js → stack-Y3pDteZr.js} +1 -1
- package/dist/dashboard/assets/stack-Y3pDteZr.js.gz +0 -0
- package/dist/dashboard/assets/{state-Cg3VL5J_.js → state-CArX_Iak.js} +1 -1
- package/dist/dashboard/assets/state-CArX_Iak.js.gz +0 -0
- package/dist/dashboard/assets/{switch-Db70BMix.js → switch-DwlwmDO-.js} +1 -1
- package/dist/dashboard/assets/switch-DwlwmDO-.js.gz +0 -0
- package/dist/dashboard/assets/{useMutation-BqdE0LuM.js → useMutation-DJ9GYEgf.js} +1 -1
- package/dist/dashboard/assets/useMutation-DJ9GYEgf.js.gz +0 -0
- package/dist/dashboard/index.html +2 -2
- package/dist/dashboard/index.html.gz +0 -0
- package/dist/dashboard/precompressed-assets.json +54 -53
- package/migrations/full-postgres/0046_add_accounts_quota_plan_fetched_at.sql +7 -0
- package/migrations/full-postgres/0047_add_provider_device_flow_proxy.sql +17 -0
- package/migrations/lite-sqlite3/0046_add_accounts_quota_plan_fetched_at.sql +4 -0
- package/migrations/lite-sqlite3/0047_add_provider_device_flow_proxy.sql +19 -0
- package/package.json +1 -1
- package/src/boot/app.ts +11 -14
- package/src/boot/dependencies.ts +5 -7
- package/src/boot/flush.ts +20 -0
- package/src/boot/lifecycle.ts +7 -5
- package/src/config/providers.ts +6 -5
- package/src/config/routing.ts +26 -0
- package/src/config/spec-metadata.ts +72 -0
- package/src/config/spec.ts +28 -8
- package/src/console/api-keys/contracts.ts +12 -1
- package/src/console/api-keys/routes.ts +40 -28
- 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/backup/nine-router.ts +71 -10
- package/src/console/backup/service.ts +8 -1
- package/src/console/cli-tools/service.ts +6 -12
- package/src/console/console-router-guards.ts +23 -16
- package/src/console/console-router.ts +1 -1
- package/src/console/domain-registration.ts +7 -4
- package/src/console/inferhub/client.ts +1 -1
- package/src/console/inferhub/normalize.ts +24 -28
- package/src/console/inferhub/types.ts +3 -0
- package/src/console/ip-bans/routes.ts +7 -13
- package/src/console/observability/live.ts +2 -2
- package/src/console/observability/store.ts +0 -53
- package/src/console/providers/catalog/contracts.ts +9 -3
- package/src/console/providers/catalog/model-operations.ts +2 -3
- package/src/console/providers/catalog/provider-operations.ts +1 -1
- package/src/console/providers/catalog/routes.ts +3 -2
- package/src/console/providers/catalog/store.ts +1914 -1902
- package/src/console/providers/detail/routes.ts +1 -0
- package/src/console/providers/detail/store.ts +4 -0
- package/src/console/providers/oauth/callback-listener.ts +30 -11
- package/src/console/providers/oauth/device-egress.ts +123 -0
- package/src/console/providers/oauth/routes.ts +123 -16
- package/src/console/quota/account-quota-global.ts +14 -24
- package/src/console/quota/account-quota-shared.ts +49 -0
- package/src/console/quota/account-quota-tenant.ts +11 -23
- package/src/console/quota/account-test-egress.ts +2 -2
- package/src/console/quota/daily-growth-job.ts +1 -1
- package/src/console/quota/quota-view.ts +19 -3
- package/src/console/routing/pools/contracts.ts +1 -1
- package/src/console/routing/pools/operations.ts +32 -47
- package/src/console/routing/pools/probe-verdict.ts +2 -2
- package/src/console/routing/pools/relay-deploy.ts +1 -1
- package/src/console/routing/pools/store.ts +4 -4
- package/src/console/shared/errors.ts +33 -6
- package/src/main.ts +2 -3
- package/src/network/client.ts +67 -0
- package/src/network/egress/bidi-session.ts +322 -0
- package/src/network/egress/dial.ts +201 -0
- package/src/network/egress/factory.ts +191 -0
- package/src/network/{outbound-fetch.ts → egress/fetch.ts} +195 -29
- package/src/network/egress/tls-profile.ts +123 -0
- package/src/network/egress/ws.ts +175 -0
- package/src/network/pool/{byte-accounting.ts → accounting.ts} +3 -2
- package/src/network/{types.ts → pool/agent-config.ts} +1 -1
- package/src/network/pool/agent.ts +2 -2
- package/src/network/{pool-cooldown-store.ts → pool/cooldown-store.ts} +95 -20
- package/src/network/{pool-health-machine.ts → pool/health.ts} +205 -6
- package/src/network/pool/resolver.ts +10 -849
- package/src/network/pool/selector.ts +184 -41
- package/src/network/ssrf.ts +7 -7
- package/src/network/{retry.ts → util/retry.ts} +1 -1
- package/src/network/{rate-limit-verdict.ts → verdict.ts} +1 -1
- package/src/observability/event-log.ts +7 -361
- package/src/observability/performance-metrics.ts +9 -0
- package/src/observability/runtime-metrics.ts +14 -9
- package/src/observability/telemetry-buffer.ts +16 -26
- 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 +18 -0
- package/src/persistence/migrate.ts +6 -3
- package/src/persistence/schema.ts +17 -0
- package/src/persistence/ttl-cache.ts +21 -3
- package/src/protocol/codecs.ts +59 -2
- package/src/protocol/dsml.ts +34 -86
- package/src/protocol/markup-stream-parser.ts +195 -0
- package/src/protocol/reasoning-text.ts +19 -0
- package/src/protocol/request/chat.ts +10 -10
- package/src/protocol/request/gemini.ts +8 -2
- package/src/protocol/request/messages.ts +34 -22
- package/src/protocol/response/chat.ts +12 -20
- package/src/protocol/response/messages.ts +34 -7
- package/src/protocol/response/omp-tool-call-markup.ts +43 -86
- package/src/protocol/response/responses.ts +18 -2
- package/src/protocol/stop-reason.ts +23 -1
- package/src/protocol/think-split.ts +112 -0
- package/src/protocol/translation/dialect/qwen.ts +26 -58
- package/src/protocol/translation/quirks.ts +15 -0
- package/src/protocol/upstream/send-claude-messages.ts +2 -2
- package/src/providers/adapters/compatible-adapter.ts +76 -12
- package/src/providers/authentication/oauth-client.ts +40 -6
- package/src/providers/authentication/oauth-device-flow.ts +105 -11
- package/src/providers/authentication/oauth-flow-store.ts +82 -1
- package/src/providers/authentication/oauth-refresh-service.ts +766 -758
- package/src/providers/configured-provider.ts +148 -0
- package/src/providers/default-registry.ts +137 -36
- package/src/providers/discovery/discovery-types.ts +7 -0
- package/src/providers/discovery/probe-phases.ts +20 -6
- package/src/providers/discovery/probing-service.ts +22 -2
- 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 +16 -1
- package/src/providers/integrations/buddy/account.ts +969 -0
- package/src/providers/integrations/buddy/capabilities.ts +12 -12
- package/src/providers/integrations/buddy/{buddy-catalog-shared.ts → catalog.ts} +198 -136
- package/src/providers/integrations/buddy/{buddy-chat-shared.ts → chat.ts} +176 -20
- package/src/providers/integrations/buddy/codebuddy/index.ts +429 -0
- package/src/providers/integrations/buddy/codebuddy/shared.ts +41 -26
- package/src/providers/integrations/buddy/headers.ts +311 -0
- package/src/providers/integrations/buddy/lifecycle.ts +1018 -0
- package/src/providers/integrations/buddy/{buddy-media.ts → media.ts} +1 -1
- package/src/providers/integrations/claude/claude-quota.ts +14 -17
- package/src/providers/integrations/cline/cline-oauth.ts +10 -5
- package/src/providers/integrations/cline/cline.ts +5 -6
- package/src/providers/integrations/clixad/clixad-oauth.ts +98 -0
- package/src/providers/integrations/clixad/clixad-quota.ts +132 -0
- package/src/providers/integrations/clixad/provider.ts +145 -0
- package/src/providers/integrations/codex/codex-oauth.ts +8 -5
- package/src/providers/integrations/commandcode/quota.ts +22 -28
- package/src/providers/integrations/cursor/cursor-auth.ts +1 -9
- package/src/providers/integrations/cursor/cursor-exec.ts +9 -2
- package/src/providers/integrations/cursor/cursor-hardening.ts +279 -0
- package/src/providers/integrations/cursor/cursor-http2.ts +605 -6
- package/src/providers/integrations/cursor/cursor-request.ts +26 -12
- package/src/providers/integrations/cursor/cursor-shared.ts +8 -5
- package/src/providers/integrations/cursor/cursor.ts +574 -110
- 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/deepai/deepai-catalog.ts +149 -0
- package/src/providers/integrations/deepai/deepai-common.ts +145 -0
- package/src/providers/integrations/deepai/deepai-oauth.ts +129 -0
- package/src/providers/integrations/deepai/deepai-quota.ts +97 -0
- package/src/providers/integrations/deepai/provider.ts +42 -0
- package/src/providers/integrations/devin/devin-auth.ts +7 -0
- package/src/providers/integrations/devin/protocol/devin-proto.ts +1 -27
- package/src/providers/integrations/factory/factory-oauth.ts +33 -51
- 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 +5 -0
- package/src/providers/integrations/freepi/freepi-discovery.ts +2 -2
- package/src/providers/integrations/freepi/freepi-headers.ts +2 -2
- package/src/providers/integrations/freepi/freepi-protocol.ts +11 -2
- package/src/providers/integrations/freepi/freepi-realm.ts +0 -3
- package/src/providers/integrations/freepi/freepi-session.ts +158 -0
- package/src/providers/integrations/freepi/freepi.ts +80 -54
- package/src/providers/integrations/github/github-discovery.ts +30 -13
- package/src/providers/integrations/github/github-oauth.ts +217 -44
- package/src/providers/integrations/github/github-quota.ts +22 -5
- package/src/providers/integrations/github/github.ts +40 -32
- package/src/providers/integrations/grok/grok-oauth.ts +52 -49
- 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 +3 -3
- package/src/providers/integrations/{hermes.ts → hermes/hermes.ts} +42 -13
- package/src/providers/integrations/hermes/index.ts +2 -0
- package/src/providers/integrations/kilo/kilo-oauth.ts +4 -4
- package/src/providers/integrations/kilo/kilo.ts +1 -1
- package/src/providers/integrations/kimi/kimi-oauth.ts +3 -3
- 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-oauth.ts +2 -2
- package/src/providers/integrations/kiro/kiro-quota.ts +8 -4
- package/src/providers/integrations/kiro/kiro.ts +15 -19
- package/src/providers/integrations/muse/muse-oauth.ts +5 -5
- 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/openrouter/provider.ts +1 -1
- package/src/providers/integrations/perch/perch-credential.ts +25 -0
- package/src/providers/integrations/perch/perch-protocol.ts +0 -6
- package/src/providers/integrations/perch/perch-quota.ts +2 -16
- package/src/providers/integrations/perch/perch.ts +6 -16
- package/src/providers/integrations/qoder/claim-qoder-credits.ts +19 -60
- package/src/providers/integrations/qoder/qoder-adapter.ts +20 -125
- package/src/providers/integrations/qoder/qoder-auth.ts +109 -30
- package/src/providers/integrations/qoder/qoder-catalog.ts +21 -6
- package/src/providers/integrations/qoder/qoder-chat.ts +32 -29
- package/src/providers/integrations/qoder/qoder-checkin.ts +5 -5
- package/src/providers/integrations/qoder/qoder-cosy.ts +37 -9
- package/src/providers/integrations/qoder/qoder-oauth.ts +9 -2
- package/src/providers/integrations/qoder/qoder-realm.ts +13 -36
- package/src/providers/integrations/qoder/qoder-sse.ts +447 -68
- package/src/providers/integrations/qoder/qoder-types.ts +9 -0
- package/src/providers/integrations/qoder/qoder.ts +3 -26
- package/src/providers/integrations/sensenova/provider.ts +144 -0
- package/src/providers/integrations/trae/trae-protocol.ts +9 -5
- package/src/providers/integrations/trae/trae-quota.ts +21 -29
- package/src/providers/integrations/trae/trae-realm.ts +38 -0
- package/src/providers/integrations/trae/trae.ts +7 -24
- package/src/providers/integrations/vercel/provider.ts +117 -0
- package/src/providers/integrations/vercel/vercel-quota.ts +96 -0
- package/src/providers/integrations/xai/xai-oauth.ts +5 -5
- package/src/providers/integrations/zcode-start-plan/gifts.ts +1 -1
- package/src/providers/integrations/zcode-start-plan/oauth.ts +3 -3
- 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 +205 -0
- package/src/providers/operations/account-error-classification.ts +199 -307
- package/src/providers/operations/account-health-service.ts +82 -1
- package/src/providers/operations/account-refresh-status.ts +36 -37
- package/src/providers/operations/account-tier.ts +15 -16
- package/src/providers/operations/client-version-resolver.ts +1 -1
- package/src/providers/operations/client-versions.ts +100 -81
- package/src/providers/operations/cooldown-rules.ts +11 -16
- package/src/providers/operations/oauth-refresh-lead.ts +46 -22
- package/src/providers/operations/{model-catalog-cache.ts → provider-caches.ts} +68 -16
- package/src/providers/operations/provider-catalog-service.ts +1 -1
- package/src/providers/operations/provider-credential-service.ts +118 -51
- 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/operations/session-resolution.ts +17 -1
- package/src/providers/provider-contracts.ts +6 -4
- package/src/providers/{provider-credential-resolver.ts → provider-credentials.ts} +40 -3
- package/src/providers/provider-metadata.ts +70 -10
- package/src/providers/provider-probe.ts +15 -1
- package/src/providers/provider-registry.ts +42 -33
- package/src/providers/quota/quota-contracts.ts +2 -0
- 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 +100 -21
- package/src/security/api-key-auth-cache.ts +1 -1
- package/src/security/api-key-auth.ts +6 -2
- package/src/security/ip-boundary.ts +1 -1
- package/src/transport/dispatch/attempt-finalize.ts +15 -14
- package/src/transport/dispatch/attempt-loop.ts +73 -19
- package/src/transport/dispatch/generation.ts +2 -8
- package/src/transport/dispatch/handler-deps.ts +1 -1
- package/src/transport/dispatch/leases.ts +1 -1
- package/src/transport/dispatch/native-shared.ts +25 -0
- package/src/transport/dispatch/proxy-request.ts +8 -46
- package/src/transport/dispatch/responses-compact.ts +2 -8
- package/src/transport/dispatch/retry-policy.ts +1 -1
- package/src/transport/dispatch/streaming/finalize.ts +10 -7
- package/src/transport/dispatch/streaming/stream.ts +9 -37
- package/src/transport/dispatch/systemone.ts +2 -8
- package/src/transport/dispatch/token-count.ts +2 -6
- package/src/transport/dispatch/upstream.ts +63 -0
- package/src/transport/drain-endpoint.ts +31 -4
- package/src/transport/failure-policy.ts +16 -30
- 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 +393 -32
- package/src/transport/shutdown-notice.ts +1 -1
- 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 +7 -1
- 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 +22 -5
- package/src/transport/surface/responses/parse.ts +3 -11
- package/src/workers/buddy-growth-tasks.ts +8 -8
- package/src/workers/daily-checkin.ts +4 -4
- package/src/workers/quota-refresh-sweep.ts +1 -1
- package/src/workers/zcode-gift-claim.ts +1 -1
- package/dist/dashboard/assets/BannedPage-BxoG1MrL.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-ixwgKk53.js.gz +0 -0
- package/dist/dashboard/assets/CliToolsPage-BmttNV3U.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-BE3qwwfC.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-CRZdmMIX.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-Dv_FJ6Er.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-CoVrw2sM.js +0 -1
- package/dist/dashboard/assets/HealthEventsModal-CoVrw2sM.js.gz +0 -0
- package/dist/dashboard/assets/InjectSystemPromptPanel-BOoO8rpz.js.gz +0 -0
- package/dist/dashboard/assets/LoginPage-ChJuNNVN.js.gz +0 -0
- package/dist/dashboard/assets/ModelPicker-BZAJtPg1.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-BE40s3u5.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-sfLoQtPV.js +0 -8
- package/dist/dashboard/assets/ProviderDetailPage-sfLoQtPV.js.gz +0 -0
- package/dist/dashboard/assets/ProviderIcon-CQ6bPUf_.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-DcepmPWo.js +0 -1
- package/dist/dashboard/assets/ProvidersPage-DcepmPWo.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-BJ87Pp0U.js +0 -9
- package/dist/dashboard/assets/ProxyPage-BJ87Pp0U.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-gkSyRAKa.js +0 -1
- package/dist/dashboard/assets/QuotaPage-gkSyRAKa.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-C5HPWqEv.js.gz +0 -0
- package/dist/dashboard/assets/SetupPage-CFNcATJJ.js.gz +0 -0
- package/dist/dashboard/assets/StudioPage-YoD4tY5l.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-DxjaAhb_.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-BTlkq_Uk.js.gz +0 -0
- package/dist/dashboard/assets/app-BX2nnKqs.css +0 -2
- package/dist/dashboard/assets/app-BX2nnKqs.css.gz +0 -0
- package/dist/dashboard/assets/assert-CndthXFQ.js +0 -1
- package/dist/dashboard/assets/assert-CndthXFQ.js.gz +0 -0
- package/dist/dashboard/assets/badge-DdbT_7Am.js.gz +0 -0
- package/dist/dashboard/assets/banner-CsGN-nbm.js.gz +0 -0
- package/dist/dashboard/assets/card-mgZ_TLLZ.js.gz +0 -0
- package/dist/dashboard/assets/cli-tools-A1-v8-03.js.gz +0 -0
- package/dist/dashboard/assets/clipboard-button-CJ4Zty89.js.gz +0 -0
- package/dist/dashboard/assets/dialog-B5L2jPQ_.js.gz +0 -0
- package/dist/dashboard/assets/drawer-DkjUouAj.js.gz +0 -0
- package/dist/dashboard/assets/entry-B-LVLV6o.js.gz +0 -0
- package/dist/dashboard/assets/entry-CAhdYl3B.js.gz +0 -0
- package/dist/dashboard/assets/entry-D2xD0Tmd.js +0 -4
- package/dist/dashboard/assets/entry-D2xD0Tmd.js.gz +0 -0
- package/dist/dashboard/assets/formatters-CrsdLVsn.js.gz +0 -0
- package/dist/dashboard/assets/index-BU64Lqvp.js.gz +0 -0
- package/dist/dashboard/assets/inline-B2aDLyyg.js.gz +0 -0
- package/dist/dashboard/assets/input-BCVkdR9d.js.gz +0 -0
- package/dist/dashboard/assets/layout-DNcAdMK-.js.gz +0 -0
- package/dist/dashboard/assets/list-row-DgrIH0hw.js.gz +0 -0
- package/dist/dashboard/assets/live-Cae01iu8.js.gz +0 -0
- package/dist/dashboard/assets/network-CRib7H_2.js.gz +0 -0
- package/dist/dashboard/assets/numbers-BKQE8Xpz.js.gz +0 -0
- package/dist/dashboard/assets/pagination-ecPvG_8g.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-Ckm1UXuC.js.gz +0 -0
- package/dist/dashboard/assets/select-ChoFWyKz.js.gz +0 -0
- package/dist/dashboard/assets/settings-CKjFPlKW.js.gz +0 -0
- package/dist/dashboard/assets/stack-DWCwU3fh.js.gz +0 -0
- package/dist/dashboard/assets/state-Cg3VL5J_.js.gz +0 -0
- package/dist/dashboard/assets/switch-Db70BMix.js.gz +0 -0
- package/dist/dashboard/assets/useMutation-BqdE0LuM.js.gz +0 -0
- package/src/network/pool-health.ts +0 -210
- package/src/providers/bundled-discovery-providers.ts +0 -98
- package/src/providers/configured-openai-providers.ts +0 -120
- package/src/providers/integrations/buddy/buddy-checkin.ts +0 -521
- package/src/providers/integrations/buddy/buddy-cli-headers.ts +0 -76
- package/src/providers/integrations/buddy/buddy-conversation-headers.ts +0 -130
- package/src/providers/integrations/buddy/buddy-discovery-shared.ts +0 -234
- package/src/providers/integrations/buddy/buddy-growth-shared.ts +0 -496
- package/src/providers/integrations/buddy/buddy-identity.ts +0 -300
- package/src/providers/integrations/buddy/buddy-oauth-shared.ts +0 -341
- package/src/providers/integrations/buddy/buddy-quota-shared.ts +0 -417
- package/src/providers/integrations/buddy/buddy-server-tools.ts +0 -240
- package/src/providers/integrations/buddy/buddy-usage-shared.ts +0 -221
- package/src/providers/integrations/buddy/buddy-usage-store.ts +0 -181
- package/src/providers/integrations/buddy/codebuddy/cn-catalog.ts +0 -37
- package/src/providers/integrations/buddy/codebuddy/cn-provider.ts +0 -185
- package/src/providers/integrations/buddy/codebuddy/oauth.ts +0 -47
- package/src/providers/integrations/buddy/codebuddy/provider.ts +0 -175
- package/src/providers/integrations/buddy/codebuddy/quota.ts +0 -154
- package/src/providers/integrations/qoder/chat-proto.ts +0 -328
- package/src/providers/integrations/qoder/qoder-grpc.ts +0 -871
- package/src/providers/integrations/qoder/qoder-ide.ts +0 -588
- 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-B7kIWFm5.js → app-C0gTXEoN.js} +0 -0
- /package/dist/dashboard/assets/{app-B7kIWFm5.js.gz → app-C0gTXEoN.js.gz} +0 -0
- /package/src/network/{http2-bidi.ts → bidi.ts} +0 -0
- /package/src/network/pool/{pool-scripts.ts → scripts.ts} +0 -0
- /package/src/network/pool/{pool-state.ts → state.ts} +0 -0
- /package/src/network/{backoff.ts → util/backoff.ts} +0 -0
- /package/src/network/{deduplication.ts → util/dedupe.ts} +0 -0
- /package/src/network/{ip-utils.ts → util/ip.ts} +0 -0
|
@@ -1,20 +1,40 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Qoder-specific SSE decoding.
|
|
2
|
+
* Qoder-specific SSE decoding + enveloped stream → canonical events.
|
|
3
3
|
*
|
|
4
4
|
* The generic `decodeSseEvents` splits on newlines and treats a bare line
|
|
5
|
-
* (no `data:` prefix) with no open event as malformed. Qoder's
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
5
|
+
* (no `data:` prefix) with no open event as malformed. Qoder's surfaces embed
|
|
6
|
+
* literal newline bytes inside JSON string values (e.g. `raw_usage` keys like
|
|
7
|
+
* `"data_policy_preferre\n\nnd_count"`), which fragments one logical `data:`
|
|
8
|
+
* event into many physical lines — including blank lines that are not event
|
|
9
|
+
* boundaries. The generic decoder can throw `Malformed SSE line` on the
|
|
10
10
|
* trailing fragments of those giant events.
|
|
11
11
|
*
|
|
12
12
|
* This decoder is tolerant by construction: it accumulates one event's payload
|
|
13
13
|
* across `data:` lines, bare continuations, and blank lines until the payload
|
|
14
14
|
* is a complete JSON value or the `[DONE]` sentinel. It never throws on a
|
|
15
15
|
* bare fragment — payload completeness is the only way an event ends.
|
|
16
|
+
*
|
|
17
|
+
* The enveloped-stream mapping (`qoderBodyToCanonicalEvents`) is the sole
|
|
18
|
+
* chat-stream parser for the live legacy COSY surface
|
|
19
|
+
* (`/algo/.../agent_chat_generation`). It understands Qoder's envelope
|
|
20
|
+
* (`{headers, body, statusCodeValue, statusCode}`), direct OpenAI chunks, and
|
|
21
|
+
* the terminal `raw_usage` event, and maps them to canonical events. The dead
|
|
22
|
+
* surfaces (gRPC `model.chat.ChatService`, IDE `/model/v1`) are not part of
|
|
23
|
+
* this module anymore.
|
|
16
24
|
*/
|
|
17
25
|
|
|
26
|
+
import { GatewayError } from "../../../protocol/gateway-error";
|
|
27
|
+
import type { CanonicalEvent, CanonicalStopReason } from "../../../protocol/canonical-model";
|
|
28
|
+
import { usageFromProvider, readReasoningText } from "../../provider-usage";
|
|
29
|
+
import {
|
|
30
|
+
classifyUpstreamError,
|
|
31
|
+
extractUpstreamMessage,
|
|
32
|
+
isUpstreamQueued,
|
|
33
|
+
queueRetryAfterMs,
|
|
34
|
+
upstreamErrorIdentifier,
|
|
35
|
+
} from "../../../transport/failure-policy";
|
|
36
|
+
import { QODER_PROVIDER_ID } from "./qoder-realm";
|
|
37
|
+
|
|
18
38
|
export interface QoderSseEvent {
|
|
19
39
|
readonly event: string | null;
|
|
20
40
|
readonly data: string;
|
|
@@ -78,82 +98,441 @@ export async function* decodeQoderSseEvents(
|
|
|
78
98
|
return null;
|
|
79
99
|
}
|
|
80
100
|
if (trimmed === "[DONE]" || isCompleteJsonValue(trimmed)) {
|
|
81
|
-
const
|
|
101
|
+
const out = current;
|
|
82
102
|
current = null;
|
|
83
103
|
eventBytes = 0;
|
|
84
|
-
return
|
|
104
|
+
return out;
|
|
85
105
|
}
|
|
86
106
|
return null;
|
|
87
107
|
};
|
|
88
108
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
109
|
+
while (true) {
|
|
110
|
+
const { done, value } = await reader.read();
|
|
111
|
+
if (signal?.aborted) return;
|
|
112
|
+
if (done) break;
|
|
113
|
+
lineBuffer += decoder.decode(value, { stream: true });
|
|
114
|
+
// Drain complete lines from the buffer into the accumulation stream.
|
|
115
|
+
let newlineIndex: number;
|
|
116
|
+
while ((newlineIndex = lineBuffer.indexOf("\n")) >= 0) {
|
|
117
|
+
let line = lineBuffer.slice(0, newlineIndex).replace(/\r$/, "");
|
|
118
|
+
lineBuffer = lineBuffer.slice(newlineIndex + 1);
|
|
119
|
+
if (line.startsWith("data:")) {
|
|
120
|
+
const payload = line.slice(5).replace(/^ /, "");
|
|
121
|
+
eventBytes += payload.length;
|
|
122
|
+
if (eventBytes > maxEventBytes) {
|
|
123
|
+
current = null;
|
|
124
|
+
eventBytes = 0;
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
if (current === null) {
|
|
128
|
+
current = payload;
|
|
129
|
+
} else {
|
|
130
|
+
current += "\n" + payload;
|
|
131
|
+
}
|
|
132
|
+
const complete = takeComplete();
|
|
133
|
+
if (complete !== null) {
|
|
134
|
+
yield { event: null, data: complete };
|
|
135
|
+
}
|
|
136
|
+
} else if (line.trim() === "") {
|
|
137
|
+
// Blank line inside a giant event is a continuation, not a boundary.
|
|
138
|
+
if (current !== null) {
|
|
139
|
+
const complete = takeComplete();
|
|
140
|
+
if (complete !== null) {
|
|
141
|
+
yield { event: null, data: complete };
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
} else if (line.startsWith("event:")) {
|
|
145
|
+
// We don't key off event names; keep the accumulated payload flowing.
|
|
146
|
+
continue;
|
|
147
|
+
} else {
|
|
148
|
+
// Bare continuation line (no `data:` prefix): treat as payload.
|
|
149
|
+
if (current !== null) {
|
|
150
|
+
current += "\n" + line;
|
|
151
|
+
const complete = takeComplete();
|
|
152
|
+
if (complete !== null) {
|
|
153
|
+
yield { event: null, data: complete };
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
// Flush remaining buffer / payload.
|
|
160
|
+
if (lineBuffer.length > 0) {
|
|
161
|
+
if (current === null) {
|
|
162
|
+
current = lineBuffer.trim();
|
|
163
|
+
} else {
|
|
164
|
+
current += "\n" + lineBuffer;
|
|
165
|
+
}
|
|
103
166
|
}
|
|
167
|
+
const complete = takeComplete();
|
|
168
|
+
if (complete !== null) yield { event: null, data: complete };
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* The human-readable text out of a Qoder error envelope.
|
|
173
|
+
*
|
|
174
|
+
* The envelope is Qoder's own wrapper around the stream, and it does not use
|
|
175
|
+
* one field for the message: the text has been seen as a nested `error`
|
|
176
|
+
* object, a bare string `body`, and a top-level `message`/`msg`. The canonical
|
|
177
|
+
* extractor covers the structured shapes; a string body and the top-level
|
|
178
|
+
* fields cover the rest. Whatever survives is bounded the same way an upstream
|
|
179
|
+
* message is, so a verbose envelope cannot blow up the public error line.
|
|
180
|
+
*/
|
|
181
|
+
function qoderEnvelopeMessage(rec: Record<string, unknown>): string {
|
|
182
|
+
const structured = extractUpstreamMessage(rec["body"]);
|
|
183
|
+
if (structured.length > 0) return structured;
|
|
184
|
+
if (typeof rec["body"] === "string" && rec["body"].trim().length > 0 && rec["body"] !== "[DONE]")
|
|
185
|
+
return rec["body"].replace(/[\r\n]+/g, " ").trim().slice(0, 500);
|
|
186
|
+
return extractUpstreamMessage(rec);
|
|
187
|
+
}
|
|
104
188
|
|
|
189
|
+
/**
|
|
190
|
+
* Splits one SSE `data:` payload into frames:
|
|
191
|
+
* - the envelope form `{"headers":...,"body":"<json>","statusCodeValue":200}`,
|
|
192
|
+
* - a direct OpenAI chunk `{"choices":[...]}`,
|
|
193
|
+
* - the `[DONE]` sentinel.
|
|
194
|
+
*
|
|
195
|
+
* A non-200 envelope is classified through the gateway error policy and thrown
|
|
196
|
+
* as a `GatewayError`, including Qoder's queued-request signal (`10605` /
|
|
197
|
+
* `isQueued`).
|
|
198
|
+
*/
|
|
199
|
+
export function qoderEnvelopeToFrames(data: string): Array<{ event: string | null; data: string }> {
|
|
200
|
+
if (data === "[DONE]") return [{ event: null, data: "[DONE]" }];
|
|
201
|
+
let envelope: unknown;
|
|
202
|
+
// Raw newlines inside JSON string values (e.g. `raw_usage` keys like
|
|
203
|
+
// `"data_policy_preferre\n\nnd_count"`) are not valid JSON, so the first
|
|
204
|
+
// parse fails; when it does, salvage the event by escaping the raw newlines
|
|
205
|
+
// inside string values and parse again.
|
|
206
|
+
let repaired = data;
|
|
105
207
|
try {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
208
|
+
envelope = JSON.parse(data);
|
|
209
|
+
} catch {
|
|
210
|
+
const attempted = escapeJsonRawNewlines(data);
|
|
211
|
+
if (attempted !== data) {
|
|
212
|
+
try {
|
|
213
|
+
envelope = JSON.parse(attempted);
|
|
214
|
+
repaired = attempted;
|
|
215
|
+
} catch {
|
|
216
|
+
envelope = undefined;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
if (typeof envelope !== "object" || envelope === null || Array.isArray(envelope)) return [];
|
|
221
|
+
const rec = envelope as Record<string, unknown>;
|
|
222
|
+
if (rec["statusCodeValue"] !== undefined && rec["statusCodeValue"] !== 200) {
|
|
223
|
+
const status = typeof rec["statusCodeValue"] === "number" ? (rec["statusCodeValue"] as number) : 502;
|
|
224
|
+
const detail = qoderEnvelopeMessage(rec);
|
|
225
|
+
const identifier = upstreamErrorIdentifier(rec["body"]) ?? upstreamErrorIdentifier(rec);
|
|
226
|
+
const queuedWaitMs = isUpstreamQueued(rec["body"]) ?? false;
|
|
227
|
+
const waitMs = queuedWaitMs
|
|
228
|
+
? (queueRetryAfterMs(rec["body"]) ?? undefined)
|
|
229
|
+
: undefined;
|
|
230
|
+
const classification = queuedWaitMs
|
|
231
|
+
? { code: "upstream_queued" as const, status: 503, origin: "upstream" as const }
|
|
232
|
+
: classifyUpstreamError(status, identifier);
|
|
233
|
+
throw new GatewayError(
|
|
234
|
+
classification.code,
|
|
235
|
+
classification.status,
|
|
236
|
+
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 🌙`,
|
|
237
|
+
{
|
|
238
|
+
providerId: QODER_PROVIDER_ID,
|
|
239
|
+
providerStatus: status,
|
|
240
|
+
...(identifier ? { providerCode: identifier } : {}),
|
|
241
|
+
...(classification.credentialEvidence ? { credentialEvidence: true } : {}),
|
|
242
|
+
...(classification.rateLimitScope
|
|
243
|
+
? { rateLimitScope: classification.rateLimitScope }
|
|
244
|
+
: {}),
|
|
245
|
+
...(waitMs === undefined ? {} : { retryAfterMs: waitMs, cooldownMs: waitMs }),
|
|
246
|
+
...(queuedWaitMs ? { reason: "upstream-queued" } : {}),
|
|
247
|
+
},
|
|
248
|
+
classification.origin,
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
const body = rec["body"];
|
|
252
|
+
if (body === "[DONE]") return [{ event: null, data: "[DONE]" }];
|
|
253
|
+
if (typeof body === "string") return [{ event: null, data: body.replace(/\r?\n/g, "") }];
|
|
254
|
+
// Direct OpenAI chunk: no envelope body, but a `choices` array is present.
|
|
255
|
+
// Pass the (repaired) raw data through so the chunk parser sees it.
|
|
256
|
+
if (Array.isArray(rec["choices"])) return [{ event: null, data: repaired }];
|
|
257
|
+
return [];
|
|
258
|
+
}
|
|
132
259
|
|
|
133
|
-
|
|
134
|
-
|
|
260
|
+
/**
|
|
261
|
+
* Escapes raw newline bytes that appear inside JSON string values.
|
|
262
|
+
*/
|
|
263
|
+
function escapeJsonRawNewlines(data: string): string {
|
|
264
|
+
let out = "";
|
|
265
|
+
let inString = false;
|
|
266
|
+
let escaped = false;
|
|
267
|
+
for (let i = 0; i < data.length; i += 1) {
|
|
268
|
+
const ch = data[i]!;
|
|
269
|
+
if (inString) {
|
|
270
|
+
if (escaped) {
|
|
271
|
+
out += ch;
|
|
272
|
+
escaped = false;
|
|
273
|
+
continue;
|
|
274
|
+
}
|
|
275
|
+
if (ch === "\\") {
|
|
276
|
+
out += ch;
|
|
277
|
+
escaped = true;
|
|
278
|
+
continue;
|
|
279
|
+
}
|
|
280
|
+
if (ch === '"') {
|
|
281
|
+
inString = false;
|
|
282
|
+
out += ch;
|
|
283
|
+
continue;
|
|
284
|
+
}
|
|
285
|
+
if (ch === "\n" || ch === "\r") {
|
|
286
|
+
out += "\\n";
|
|
287
|
+
continue;
|
|
288
|
+
}
|
|
289
|
+
out += ch;
|
|
290
|
+
continue;
|
|
291
|
+
}
|
|
292
|
+
if (ch === '"') {
|
|
293
|
+
inString = true;
|
|
294
|
+
out += ch;
|
|
295
|
+
continue;
|
|
296
|
+
}
|
|
297
|
+
out += ch;
|
|
298
|
+
}
|
|
299
|
+
return out;
|
|
300
|
+
}
|
|
135
301
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
302
|
+
function mapQoderFinishReason(raw: unknown): CanonicalStopReason | undefined {
|
|
303
|
+
if (raw === "stop") return "stop";
|
|
304
|
+
if (raw === "length") return "length";
|
|
305
|
+
if (raw === "tool_calls") return "tool_use";
|
|
306
|
+
if (raw === "content_filter") return "content_filter";
|
|
307
|
+
if (raw === "error") return "error";
|
|
308
|
+
if (raw === "refusal") return "refusal";
|
|
309
|
+
return undefined;
|
|
310
|
+
}
|
|
143
311
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
312
|
+
/**
|
|
313
|
+
* Keys the stream's `raw_usage` (and top-level frame) carries that describe the
|
|
314
|
+
* model that *actually* served the turn. The production COSY surface only
|
|
315
|
+
* echoes an opaque roster key, so this metadata is the one place the upstream
|
|
316
|
+
* names its real supply model/target.
|
|
317
|
+
*/
|
|
318
|
+
const QODER_PROVIDER_META_KEYS = [
|
|
319
|
+
"provider_model",
|
|
320
|
+
"from_model",
|
|
321
|
+
"supply_model",
|
|
322
|
+
"supply_account",
|
|
323
|
+
"supply_region",
|
|
324
|
+
"model_level",
|
|
325
|
+
"router_strategy_name",
|
|
326
|
+
"router_strategy_type",
|
|
327
|
+
"scheduler_times",
|
|
328
|
+
"target",
|
|
329
|
+
"target_id",
|
|
330
|
+
] as const;
|
|
331
|
+
|
|
332
|
+
/** Collects provider metadata from a raw_usage object and its `data` child. */
|
|
333
|
+
function collectQoderProviderMeta(
|
|
334
|
+
target: Record<string, unknown>,
|
|
335
|
+
rawUsageObj: Record<string, unknown>,
|
|
336
|
+
): void {
|
|
337
|
+
const data = rawUsageObj["data"];
|
|
338
|
+
const candidates: unknown[] = [rawUsageObj, data];
|
|
339
|
+
for (const src of candidates) {
|
|
340
|
+
if (!src || typeof src !== "object" || Array.isArray(src)) continue;
|
|
341
|
+
const s = src as Record<string, unknown>;
|
|
342
|
+
for (const key of QODER_PROVIDER_META_KEYS) {
|
|
343
|
+
const value = s[key];
|
|
344
|
+
if (value !== undefined && value !== null && target[key] === undefined) {
|
|
345
|
+
target[key] = value;
|
|
152
346
|
}
|
|
153
347
|
}
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Maps one Qoder stream (legacy COSY / enveloped SSE) to canonical events.
|
|
353
|
+
*
|
|
354
|
+
* This is the only live chat-stream parser (2026-11-11): the gRPC and IDE
|
|
355
|
+
* surfaces are dead on every resolving host, so `qoderBodyToCanonicalEvents`
|
|
356
|
+
* is the sole path used by the adapter.
|
|
357
|
+
*/
|
|
358
|
+
export async function* qoderBodyToCanonicalEvents(
|
|
359
|
+
body: ReadableStream<Uint8Array>,
|
|
360
|
+
signal: AbortSignal,
|
|
361
|
+
model: string,
|
|
362
|
+
): AsyncIterable<CanonicalEvent> {
|
|
363
|
+
let seq = 1;
|
|
364
|
+
let started = false;
|
|
365
|
+
let finishReason: unknown = undefined;
|
|
366
|
+
let rawUsage: Record<string, unknown> | undefined = undefined;
|
|
367
|
+
const providerMetadata: Record<string, unknown> = {};
|
|
368
|
+
let hasTerminal = false;
|
|
369
|
+
// Once a finish reason or `[DONE]` is seen the turn is over: delta content,
|
|
370
|
+
// reasoning, and tool-call fragments in later frames must be dropped —
|
|
371
|
+
// strict OpenAI clients reject content after the finish reason. Usage,
|
|
372
|
+
// errors, and the marker itself still flow.
|
|
373
|
+
let finishSeen = false;
|
|
374
|
+
/** First-sighting id per wire tool-call index, for continuation chunks. */
|
|
375
|
+
const indexIds = new Map<number, string>();
|
|
376
|
+
/** Last seen call id, for index-less continuation fragments. */
|
|
377
|
+
let lastSeenToolId: string | undefined = undefined;
|
|
154
378
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
379
|
+
for await (const sse of decodeQoderSseEvents(body, signal)) {
|
|
380
|
+
let frames: Array<{ event: string | null; data: string }>;
|
|
381
|
+
try {
|
|
382
|
+
frames = qoderEnvelopeToFrames(sse.data);
|
|
383
|
+
} catch (e) {
|
|
384
|
+
throw e;
|
|
385
|
+
}
|
|
386
|
+
for (const frame of frames) {
|
|
387
|
+
const data = frame.data.trim();
|
|
388
|
+
if (!data) continue;
|
|
389
|
+
if (data === "[DONE]") {
|
|
390
|
+
hasTerminal = true;
|
|
391
|
+
finishSeen = true;
|
|
392
|
+
if (!started) {
|
|
393
|
+
yield { type: "response_start", sequence_number: seq++, model } as CanonicalEvent;
|
|
394
|
+
started = true;
|
|
395
|
+
}
|
|
396
|
+
continue;
|
|
397
|
+
}
|
|
398
|
+
let json: Record<string, unknown>;
|
|
399
|
+
try {
|
|
400
|
+
json = JSON.parse(data) as Record<string, unknown>;
|
|
401
|
+
} catch {
|
|
402
|
+
continue;
|
|
403
|
+
}
|
|
404
|
+
// The upstream names the real model in each chunk. Use the first one we
|
|
405
|
+
// see for the opening event so the client sees the same model the
|
|
406
|
+
// provider actually served, not the (virtual) id that was requested.
|
|
407
|
+
if (!started && typeof json["model"] === "string") {
|
|
408
|
+
const upstreamModel = (json["model"] as string).trim();
|
|
409
|
+
yield {
|
|
410
|
+
type: "response_start",
|
|
411
|
+
sequence_number: seq++,
|
|
412
|
+
model: upstreamModel.length > 0 ? upstreamModel : model,
|
|
413
|
+
} as CanonicalEvent;
|
|
414
|
+
started = true;
|
|
415
|
+
}
|
|
416
|
+
// Error event: `{"code":"invalid_model_error","message":"..."}` carries
|
|
417
|
+
// no choices. Surface it as an upstream failure instead of an empty
|
|
418
|
+
// successful stream.
|
|
419
|
+
if (json["error"] !== undefined && json["choices"] === undefined) {
|
|
420
|
+
const error = json["error"] as Record<string, unknown> | string;
|
|
421
|
+
const message =
|
|
422
|
+
typeof error === "string"
|
|
423
|
+
? error
|
|
424
|
+
: typeof error?.["message"] === "string"
|
|
425
|
+
? (error["message"] as string)
|
|
426
|
+
: typeof json["message"] === "string"
|
|
427
|
+
? (json["message"] as string)
|
|
428
|
+
: "Qoder model server error";
|
|
429
|
+
const code = typeof json["code"] === "string" ? (json["code"] as string) : undefined;
|
|
430
|
+
throw new GatewayError(
|
|
431
|
+
code === "invalid_model_error" ? "model_not_found" : "platform_unavailable",
|
|
432
|
+
code === "invalid_model_error" ? 404 : 502,
|
|
433
|
+
`${message} (request {requestId}). The upstream came back empty-handed — retry 🌙`,
|
|
434
|
+
{ providerId: QODER_PROVIDER_ID, ...(code === undefined ? {} : { providerCode: code }) },
|
|
435
|
+
"upstream",
|
|
436
|
+
);
|
|
437
|
+
}
|
|
438
|
+
if (json["usage"] && typeof json["usage"] === "object") rawUsage = json["usage"] as Record<string, unknown>;
|
|
439
|
+
// Terminal event: `choices:[]` + `raw_usage`. It carries the final usage
|
|
440
|
+
// and confirms the turn ended, so record it and synthesize a stop reason —
|
|
441
|
+
// the terminal event has no `choices[].finish_reason`.
|
|
442
|
+
if (json["raw_usage"] && typeof json["raw_usage"] === "object") {
|
|
443
|
+
const rawUsageObj = json["raw_usage"] as Record<string, unknown>;
|
|
444
|
+
collectQoderProviderMeta(providerMetadata, rawUsageObj);
|
|
445
|
+
rawUsage = (rawUsageObj["data"] as Record<string, unknown> | undefined) ?? rawUsageObj;
|
|
446
|
+
if (json["choices"] === undefined || (json["choices"] as unknown[]).length === 0) {
|
|
447
|
+
finishReason = finishReason ?? "stop";
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
const choices = (json["choices"] as Array<Record<string, unknown>>) ?? [];
|
|
451
|
+
// After a finish was seen (per-choice or top-level finish_reason, or
|
|
452
|
+
// `[DONE]`) the turn is over: delta content, reasoning, and tool-call
|
|
453
|
+
// fragments in later frames are dropped. Usage and errors still flow.
|
|
454
|
+
if (!finishSeen) {
|
|
455
|
+
for (const choice of choices) {
|
|
456
|
+
const delta = choice["delta"] as Record<string, unknown> | undefined;
|
|
457
|
+
if (delta) {
|
|
458
|
+
if (typeof delta["content"] === "string" && (delta["content"] as string).length > 0) {
|
|
459
|
+
const piece = delta["content"] as string;
|
|
460
|
+
yield {
|
|
461
|
+
type: "content_delta",
|
|
462
|
+
sequence_number: seq++,
|
|
463
|
+
content: { kind: "text", text: piece },
|
|
464
|
+
} as CanonicalEvent;
|
|
465
|
+
continue;
|
|
466
|
+
}
|
|
467
|
+
const reasoningText = readReasoningText(delta);
|
|
468
|
+
if (typeof reasoningText === "string" && reasoningText.length > 0) {
|
|
469
|
+
yield {
|
|
470
|
+
type: "content_delta",
|
|
471
|
+
sequence_number: seq++,
|
|
472
|
+
content: { kind: "reasoning", payload: null, summary: reasoningText },
|
|
473
|
+
} as CanonicalEvent;
|
|
474
|
+
continue;
|
|
475
|
+
}
|
|
476
|
+
const toolCalls = delta["tool_calls"] as Array<Record<string, unknown>> | undefined;
|
|
477
|
+
if (toolCalls) {
|
|
478
|
+
for (const tc of toolCalls) {
|
|
479
|
+
const fn = tc["function"] as Record<string, unknown> | undefined;
|
|
480
|
+
const index = typeof tc["index"] === "number" ? (tc["index"] as number) : undefined;
|
|
481
|
+
const declaredId =
|
|
482
|
+
typeof tc["id"] === "string" && (tc["id"] as string).length > 0
|
|
483
|
+
? (tc["id"] as string)
|
|
484
|
+
: undefined;
|
|
485
|
+
const fragName = typeof fn?.["name"] === "string" ? (fn["name"] as string).trim() : undefined;
|
|
486
|
+
const fragArgs =
|
|
487
|
+
typeof fn?.["arguments"] === "string" ? (fn["arguments"] as string) : "";
|
|
488
|
+
if (declaredId !== undefined && index !== undefined && !indexIds.has(index)) {
|
|
489
|
+
indexIds.set(index, declaredId);
|
|
490
|
+
}
|
|
491
|
+
if (declaredId !== undefined) lastSeenToolId = declaredId;
|
|
492
|
+
const callId =
|
|
493
|
+
(index !== undefined ? indexIds.get(index) : undefined) ??
|
|
494
|
+
declaredId ??
|
|
495
|
+
lastSeenToolId ??
|
|
496
|
+
`call_anon_${Date.now().toString(36)}_${seq}`;
|
|
497
|
+
yield {
|
|
498
|
+
type: "tool_call_delta",
|
|
499
|
+
sequence_number: seq++,
|
|
500
|
+
call_id: callId,
|
|
501
|
+
index: index ?? 0,
|
|
502
|
+
name: fragName,
|
|
503
|
+
arguments_delta: fragArgs,
|
|
504
|
+
} as CanonicalEvent;
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
const fr = choice["finish_reason"];
|
|
509
|
+
if (fr !== null && fr !== undefined && fr !== "") {
|
|
510
|
+
finishReason = fr;
|
|
511
|
+
finishSeen = true;
|
|
512
|
+
}
|
|
513
|
+
if (choice["usage"] && typeof choice["usage"] === "object") rawUsage = choice["usage"] as Record<string, unknown>;
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
if (json["finish_reason"] !== undefined && json["finish_reason"] !== "") {
|
|
517
|
+
finishReason = json["finish_reason"];
|
|
518
|
+
finishSeen = true;
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
if (!started) {
|
|
523
|
+
yield { type: "response_start", sequence_number: seq++, model } as CanonicalEvent;
|
|
158
524
|
}
|
|
525
|
+
if (hasTerminal && finishReason === undefined) finishReason = "stop";
|
|
526
|
+
const stopReason = mapQoderFinishReason(finishReason);
|
|
527
|
+
const _providerStop = typeof finishReason === "string" ? finishReason : undefined;
|
|
528
|
+
void hasTerminal;
|
|
529
|
+
yield {
|
|
530
|
+
type: "terminal",
|
|
531
|
+
sequence_number: seq++,
|
|
532
|
+
state: stopReason === "error" ? "failed" : "complete",
|
|
533
|
+
...(stopReason === undefined ? {} : { stop_reason: stopReason }),
|
|
534
|
+
...(_providerStop === undefined ? {} : { provider_stop_reason: _providerStop }),
|
|
535
|
+
usage: usageFromProvider(rawUsage),
|
|
536
|
+
...(Object.keys(providerMetadata).length > 0 ? { provider_metadata: providerMetadata } : {}),
|
|
537
|
+
} as CanonicalEvent;
|
|
159
538
|
}
|
|
@@ -26,6 +26,15 @@ export interface QoderAuth {
|
|
|
26
26
|
machineType?: string;
|
|
27
27
|
/** Native UMID machine code (18-hex), when known. */
|
|
28
28
|
machineCode?: string;
|
|
29
|
+
/**
|
|
30
|
+
* The resolved egress IP for the HTTPDNS-managed route, when known. The
|
|
31
|
+
* desktop client stamps this on requests (`X-Qoder-HTTPDNS-IP`, worker
|
|
32
|
+
* `networkMode=httpdns`) once it has resolved the host through the Aliyun
|
|
33
|
+
* HTTPDNS SDK. Mirroring it makes the outbound fingerprint identical to the
|
|
34
|
+
* app's; absent (e.g. direct DNS) the header is omitted — the upstream then
|
|
35
|
+
* sees the same shape as a client that did not use HTTPDNS.
|
|
36
|
+
*/
|
|
37
|
+
httpDnsIp?: string;
|
|
29
38
|
}
|
|
30
39
|
|
|
31
40
|
export interface QoderModelConfig {
|
|
@@ -28,6 +28,7 @@ export {
|
|
|
28
28
|
qoderEncodeBody,
|
|
29
29
|
qoderSignerFor,
|
|
30
30
|
} from "./qoder-cosy";
|
|
31
|
+
export type { QoderCosyProfile } from "./qoder-cosy";
|
|
31
32
|
|
|
32
33
|
// Shared message helpers
|
|
33
34
|
export { flattenCanonicalContent, qoderTools, qoderToolChoice } from "./qoder-body";
|
|
@@ -37,27 +38,6 @@ export { buildQoderRequest, callQoder } from "./qoder-chat";
|
|
|
37
38
|
export type { QoderModeProfile } from "./qoder-chat";
|
|
38
39
|
export { MODERN_PROFILE } from "./qoder-chat";
|
|
39
40
|
|
|
40
|
-
// IDE surface (probe/testing only)
|
|
41
|
-
export {
|
|
42
|
-
buildQoderIdeRequest,
|
|
43
|
-
qoderBodyToCanonicalEvents,
|
|
44
|
-
qoderEnvelopeToFrames,
|
|
45
|
-
fetchQoderIdeWithEmptyRecovery,
|
|
46
|
-
} from "./qoder-ide";
|
|
47
|
-
|
|
48
|
-
// gRPC transport
|
|
49
|
-
export {
|
|
50
|
-
buildQoderGrpcRequest,
|
|
51
|
-
qoderGrpcMetadata,
|
|
52
|
-
qoderGrpcToCanonicalEvents,
|
|
53
|
-
qoderGrpcClient,
|
|
54
|
-
qoderGrpcChannelOptions,
|
|
55
|
-
fromProtoStruct,
|
|
56
|
-
QoderToolCallBuffer,
|
|
57
|
-
resolveQoderToolFragmentId,
|
|
58
|
-
} from "./qoder-grpc";
|
|
59
|
-
export type { QoderProtoToolFragment, QoderDrainedToolCall, QoderToolFragmentState } from "./qoder-grpc";
|
|
60
|
-
|
|
61
41
|
// Adapter + transport selection
|
|
62
42
|
export { createQoderAdapter, createQoderCnAdapter, resolveQoderTransport } from "./qoder-adapter";
|
|
63
43
|
|
|
@@ -65,11 +45,8 @@ export { createQoderAdapter, createQoderCnAdapter, resolveQoderTransport } from
|
|
|
65
45
|
export {
|
|
66
46
|
QODER_CHAT_PATH,
|
|
67
47
|
QODER_MODELS_PATH,
|
|
68
|
-
QODER_IDE_CHAT_PATH,
|
|
69
48
|
QODER_REALMS,
|
|
70
49
|
QODER_PROVIDER_ID,
|
|
71
|
-
qoderIdeChatUrl,
|
|
72
|
-
qoderGrpcHost,
|
|
73
50
|
qoderRealmForProvider,
|
|
74
51
|
qoderGatewayCandidates,
|
|
75
52
|
qoderInferenceCandidates,
|
|
@@ -142,6 +119,6 @@ export {
|
|
|
142
119
|
export { claimQoderCredits } from "./claim-qoder-credits";
|
|
143
120
|
export type { QoderCreditClaimResult } from "./claim-qoder-credits";
|
|
144
121
|
|
|
145
|
-
// SSE decode
|
|
146
|
-
export { decodeQoderSseEvents } from "./qoder-sse";
|
|
122
|
+
// SSE decode + enveloped stream → canonical events (legacy COSY surface)
|
|
123
|
+
export { decodeQoderSseEvents, qoderBodyToCanonicalEvents, qoderEnvelopeToFrames } from "./qoder-sse";
|
|
147
124
|
export type { QoderSseEvent } from "./qoder-sse";
|