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
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
* independent: retain the balance or subscription when the other is unavailable.
|
|
16
16
|
*/
|
|
17
17
|
import type { FetchLike, ProviderQuotaResult, ProviderQuotaWindow } from "../../quota/quota-contracts";
|
|
18
|
+
import { isoDate } from "../../quota/quota-contracts";
|
|
19
|
+
import { asCoercedNumber, asRecord } from "../../../shared/coerce";
|
|
18
20
|
|
|
19
21
|
const COMMANDCODE_API_BASE = "https://api.commandcode.ai" as const;
|
|
20
22
|
const REQUEST_TIMEOUT_MS = 15_000;
|
|
@@ -73,29 +75,21 @@ async function readJson(
|
|
|
73
75
|
}
|
|
74
76
|
}
|
|
75
77
|
|
|
76
|
-
|
|
77
|
-
return typeof value === "object" && value !== null && !Array.isArray(value)
|
|
78
|
-
? (value as Record<string, unknown>)
|
|
79
|
-
: undefined;
|
|
80
|
-
}
|
|
81
|
-
|
|
78
|
+
/** Numeric value coerced via the shared helper, or `null` (shared sentinel). */
|
|
82
79
|
function numberOrNull(value: unknown): number | null {
|
|
83
|
-
|
|
84
|
-
if (typeof value === "string" && value.trim() && Number.isFinite(Number(value))) {
|
|
85
|
-
return Number(value);
|
|
86
|
-
}
|
|
87
|
-
return null;
|
|
80
|
+
return asCoercedNumber(value) ?? null;
|
|
88
81
|
}
|
|
89
82
|
|
|
83
|
+
/**
|
|
84
|
+
* ISO timestamp from a numeric epoch (seconds vs ms via the 1e12 boundary) or
|
|
85
|
+
* a parseable date string, else `null` — the shared `isoDate` semantics. The
|
|
86
|
+
* one local delta kept: a non-positive numeric is `null` (no deadline) rather
|
|
87
|
+
* than an epoch 0/negative instant.
|
|
88
|
+
*/
|
|
90
89
|
function isoTimestamp(value: unknown): string | null {
|
|
91
90
|
const numeric = numberOrNull(value);
|
|
92
|
-
if (numeric !== null)
|
|
93
|
-
|
|
94
|
-
const date = new Date(numeric > 1_000_000_000_000 ? numeric : numeric * 1000);
|
|
95
|
-
return Number.isFinite(date.getTime()) ? date.toISOString() : null;
|
|
96
|
-
}
|
|
97
|
-
if (typeof value !== "string" || !Number.isFinite(Date.parse(value))) return null;
|
|
98
|
-
return new Date(value).toISOString();
|
|
91
|
+
if (numeric !== null && numeric <= 0) return null;
|
|
92
|
+
return isoDate(value);
|
|
99
93
|
}
|
|
100
94
|
|
|
101
95
|
function withQuery(path: string, params: Readonly<Record<string, string | null>>): string {
|
|
@@ -149,8 +143,8 @@ function windowLimits(
|
|
|
149
143
|
limits: Record<string, unknown>,
|
|
150
144
|
): readonly ProviderQuotaWindow[] {
|
|
151
145
|
const out: ProviderQuotaWindow[] = [];
|
|
152
|
-
const fiveHour =
|
|
153
|
-
const weekly =
|
|
146
|
+
const fiveHour = asRecord(limits["fiveHour"]);
|
|
147
|
+
const weekly = asRecord(limits["weekly"]);
|
|
154
148
|
const push = (source: Record<string, unknown> | undefined, kind: string, label: string) => {
|
|
155
149
|
if (source === undefined) return;
|
|
156
150
|
const used = numberOrNull(source["used"]) ?? 0;
|
|
@@ -191,9 +185,9 @@ export async function fetchCommandCodeQuota(
|
|
|
191
185
|
};
|
|
192
186
|
}
|
|
193
187
|
|
|
194
|
-
const whoamiBody = whoami.ok ?
|
|
195
|
-
const user =
|
|
196
|
-
const organization =
|
|
188
|
+
const whoamiBody = whoami.ok ? asRecord(whoami.value) : undefined;
|
|
189
|
+
const user = asRecord(whoamiBody?.["user"]);
|
|
190
|
+
const organization = asRecord(whoamiBody?.["org"]);
|
|
197
191
|
const orgId = typeof organization?.["id"] === "string" ? organization["id"] : null;
|
|
198
192
|
const accountLabel =
|
|
199
193
|
(typeof user?.["email"] === "string" && user["email"]) ||
|
|
@@ -215,8 +209,8 @@ export async function fetchCommandCodeQuota(
|
|
|
215
209
|
};
|
|
216
210
|
}
|
|
217
211
|
|
|
218
|
-
const subscriptionBody = subscriptionRaw.ok ?
|
|
219
|
-
const planRecord =
|
|
212
|
+
const subscriptionBody = subscriptionRaw.ok ? asRecord(subscriptionRaw.value) : undefined;
|
|
213
|
+
const planRecord = asRecord(subscriptionBody?.["data"]);
|
|
220
214
|
const planId = typeof planRecord?.["planId"] === "string" ? planRecord["planId"] : null;
|
|
221
215
|
const parsedPlan = planId === null ? null : commandCodePlan(planId);
|
|
222
216
|
const plan =
|
|
@@ -225,12 +219,12 @@ export async function fetchCommandCodeQuota(
|
|
|
225
219
|
(creditsRaw.ok ? "Credits" : null);
|
|
226
220
|
const periodEnd = isoTimestamp(planRecord?.["currentPeriodEnd"]);
|
|
227
221
|
|
|
228
|
-
const creditsBody = creditsRaw.ok ?
|
|
222
|
+
const creditsBody = creditsRaw.ok ? asRecord(creditsRaw.value) : undefined;
|
|
229
223
|
const windows: ProviderQuotaWindow[] = [];
|
|
230
224
|
if (creditsBody !== undefined) {
|
|
231
|
-
const credits =
|
|
225
|
+
const credits = asRecord(creditsBody["credits"]);
|
|
232
226
|
if (credits !== undefined) windows.push(...creditWindows(credits, parsedPlan?.cap ?? null, periodEnd));
|
|
233
|
-
const limits =
|
|
227
|
+
const limits = asRecord(creditsBody["windowLimits"]);
|
|
234
228
|
if (limits !== undefined) windows.push(...windowLimits(limits));
|
|
235
229
|
}
|
|
236
230
|
|
|
@@ -41,7 +41,6 @@ import {
|
|
|
41
41
|
CURSOR_WEBSITE_URL,
|
|
42
42
|
SESSION_REFRESH_TIMEOUT_MS,
|
|
43
43
|
SIGN_IN_POLICY_VIOLATION,
|
|
44
|
-
TOKEN_EXPIRY_THRESHOLD_S,
|
|
45
44
|
isExchangeableApiKey,
|
|
46
45
|
} from "./cursor-shared";
|
|
47
46
|
|
|
@@ -92,13 +91,6 @@ export function decodeJwtExpiryMs(token: string): number | undefined {
|
|
|
92
91
|
return exp * 1000;
|
|
93
92
|
}
|
|
94
93
|
|
|
95
|
-
/** True when a JWT expires within the refresh threshold. */
|
|
96
|
-
export function isExpiringSoon(token: string, thresholdSeconds = TOKEN_EXPIRY_THRESHOLD_S): boolean {
|
|
97
|
-
const expiry = decodeJwtExpiryMs(token);
|
|
98
|
-
if (expiry === undefined) return false;
|
|
99
|
-
return expiry - Date.now() < thresholdSeconds * 1000;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
94
|
/**
|
|
103
95
|
* The account identity Cursor itself asserts, from the access token's `sub`.
|
|
104
96
|
*
|
|
@@ -287,7 +279,7 @@ export class CursorOAuthClient extends OAuthDeviceFlow {
|
|
|
287
279
|
`${CURSOR_API_BASE_URL}/auth/poll?uuid=${encodeURIComponent(deviceAuthId)}` +
|
|
288
280
|
`&verifier=${encodeURIComponent(state.verifier)}`;
|
|
289
281
|
const outcome = await fetchJson(
|
|
290
|
-
asFetchFn(this.
|
|
282
|
+
asFetchFn(this.fetchFor(context)),
|
|
291
283
|
url,
|
|
292
284
|
{ method: "GET", headers: { accept: "application/json" } },
|
|
293
285
|
AUTH_REQUEST_TIMEOUT_MS,
|
|
@@ -389,15 +389,22 @@ function globFiles(args: Record<string, unknown>, root: string): Record<string,
|
|
|
389
389
|
return { files: out };
|
|
390
390
|
}
|
|
391
391
|
|
|
392
|
-
/**
|
|
392
|
+
/**
|
|
393
|
+
* Minimal glob matcher: `*` (within a segment), `**` (any depth), `?`.
|
|
394
|
+
* The value is normalized to forward slashes first, so a Windows `\` path
|
|
395
|
+
* matches exactly like a POSIX `/` path — otherwise `*.ts` matches
|
|
396
|
+
* `src\a.ts` on Windows but not `src/a.ts`, and the same pattern behaves
|
|
397
|
+
* differently depending on which separator `relative()` happened to emit.
|
|
398
|
+
*/
|
|
393
399
|
function simpleGlobMatch(pattern: string, value: string): boolean {
|
|
400
|
+
const normalized = value.replace(/\\/g, "/");
|
|
394
401
|
const re = pattern
|
|
395
402
|
.replace(/[.+^${}()|[\]\\]/g, "\\$&")
|
|
396
403
|
.replace(/\*\*/g, "__GLOBSTAR__")
|
|
397
404
|
.replace(/\*/g, "[^/]*")
|
|
398
405
|
.replace(/__GLOBSTAR__/g, ".*")
|
|
399
406
|
.replace(/\?/g, ".");
|
|
400
|
-
return new RegExp(`^${re}$`).test(
|
|
407
|
+
return new RegExp(`^${re}$`).test(normalized);
|
|
401
408
|
}
|
|
402
409
|
|
|
403
410
|
function grepFiles(args: Record<string, unknown>, root: string, pi: boolean): Record<string, unknown> {
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
// Cursor Run hardening, adapted end-to-end from oh-my-pi's streamCursorWithWireMode.
|
|
2
|
+
//
|
|
3
|
+
// The wire opens in cursor-http2.ts; the lifecycle lives in the adapter. These
|
|
4
|
+
// are the policies that make a tool loop reliable: classify a transport
|
|
5
|
+
// failure once (a model-not-found is never retried, an HTTP/2 block is never
|
|
6
|
+
// retried as HTTP/2), keep checkpoint/exec accounting across a stream's
|
|
7
|
+
// exits, and drain async handlers on BOTH exits before anything is finalized.
|
|
8
|
+
import { GatewayError } from "../../../protocol/gateway-error";
|
|
9
|
+
|
|
10
|
+
/** One decoded frame's shape — no text, tokens, ids, or arguments. */
|
|
11
|
+
export interface CursorFrameShape {
|
|
12
|
+
readonly at: number;
|
|
13
|
+
readonly kind: string;
|
|
14
|
+
readonly fields: readonly number[];
|
|
15
|
+
readonly size: number;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** The last checkpoint a Run reported, and the calls it owns. */
|
|
19
|
+
export interface CursorCheckpointState {
|
|
20
|
+
/** Checkpoint number in this Run. */
|
|
21
|
+
index: number;
|
|
22
|
+
/** Checkpoint number within its step. */
|
|
23
|
+
step: number;
|
|
24
|
+
/** Consecutive checkpoints with no semantic frame between them. */
|
|
25
|
+
streak: number;
|
|
26
|
+
/** Shapes of the frames preceding the last checkpoint. */
|
|
27
|
+
kinds: readonly string[];
|
|
28
|
+
/** Opaque bytes to resume from, when the Run must be rebuilt. */
|
|
29
|
+
bytes: Uint8Array | undefined;
|
|
30
|
+
/** Calls the server requested but this client has not answered. */
|
|
31
|
+
pendingCallIds: Set<string>;
|
|
32
|
+
/** Calls the server already counted: never re-submit these. */
|
|
33
|
+
completedCallIds: Set<string>;
|
|
34
|
+
/** Calls Cursor served itself, already in its own tool count. */
|
|
35
|
+
serverOwnedCount: number;
|
|
36
|
+
/** `tool_requests_listed.call_count`, when the edge declared it. */
|
|
37
|
+
expectedCallCount: number | undefined;
|
|
38
|
+
/** Calls this client dispatched to its executor. */
|
|
39
|
+
dispatchedCount: number;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function createCheckpointState(): CursorCheckpointState {
|
|
43
|
+
return {
|
|
44
|
+
index: 0,
|
|
45
|
+
step: 0,
|
|
46
|
+
streak: 0,
|
|
47
|
+
kinds: [],
|
|
48
|
+
bytes: undefined,
|
|
49
|
+
pendingCallIds: new Set<string>(),
|
|
50
|
+
completedCallIds: new Set<string>(),
|
|
51
|
+
serverOwnedCount: 0,
|
|
52
|
+
expectedCallCount: undefined,
|
|
53
|
+
dispatchedCount: 0,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** The first meaningful key of a decoded AgentServerMessage, for traces. */
|
|
58
|
+
export function frameKindOf(message: Record<string, unknown>): string {
|
|
59
|
+
const keys = Object.keys(message).filter((key) => message[key] !== undefined && key !== "__bytes");
|
|
60
|
+
return keys[0] ?? "unknown";
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** Shape-only trace entry: sizes and timings, never payload text or ids. */
|
|
64
|
+
export function frameShapeOf(message: Record<string, unknown>, at = Date.now()): CursorFrameShape {
|
|
65
|
+
const bytes = message["__bytes"] instanceof Uint8Array ? (message["__bytes"] as Uint8Array) : undefined;
|
|
66
|
+
return {
|
|
67
|
+
at,
|
|
68
|
+
kind: frameKindOf(message),
|
|
69
|
+
fields: bytes === undefined ? [] : topLevelFieldNumbers(bytes),
|
|
70
|
+
size: bytes?.byteLength ?? 0,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Top-level protobuf field numbers of one decoded frame's raw bytes. */
|
|
75
|
+
export function topLevelFieldNumbers(bytes: Uint8Array): number[] {
|
|
76
|
+
const numbers: number[] = [];
|
|
77
|
+
let at = 0;
|
|
78
|
+
while (at < bytes.byteLength && numbers.length < 64) {
|
|
79
|
+
let tag = 0;
|
|
80
|
+
let shift = 0;
|
|
81
|
+
for (;;) {
|
|
82
|
+
if (at >= bytes.byteLength) return numbers;
|
|
83
|
+
const byte = bytes[at] ?? 0;
|
|
84
|
+
at += 1;
|
|
85
|
+
tag |= (byte & 0x7f) << shift;
|
|
86
|
+
if ((byte & 0x80) === 0) break;
|
|
87
|
+
shift += 7;
|
|
88
|
+
if (shift > 35) return numbers;
|
|
89
|
+
}
|
|
90
|
+
const field = tag >>> 3;
|
|
91
|
+
if (field > 0) numbers.push(field);
|
|
92
|
+
const wire = tag & 7;
|
|
93
|
+
if (wire === 0) {
|
|
94
|
+
while (at < bytes.byteLength && ((bytes[at] ?? 0) & 0x80) !== 0) at += 1;
|
|
95
|
+
at += 1;
|
|
96
|
+
} else if (wire === 1) at += 8;
|
|
97
|
+
else if (wire === 2) {
|
|
98
|
+
let length = 0;
|
|
99
|
+
shift = 0;
|
|
100
|
+
for (;;) {
|
|
101
|
+
if (at >= bytes.byteLength) return numbers;
|
|
102
|
+
const byte = bytes[at] ?? 0;
|
|
103
|
+
at += 1;
|
|
104
|
+
length |= (byte & 0x7f) << shift;
|
|
105
|
+
if ((byte & 0x80) === 0) break;
|
|
106
|
+
shift += 7;
|
|
107
|
+
if (shift > 35) return numbers;
|
|
108
|
+
}
|
|
109
|
+
at += length;
|
|
110
|
+
} else if (wire === 5) at += 4;
|
|
111
|
+
else return numbers;
|
|
112
|
+
}
|
|
113
|
+
return numbers;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** Max in-memory shape-only trace entries before a failed Run is flushed. */
|
|
117
|
+
export const CURSOR_WIRE_TRACE_MAX = 256;
|
|
118
|
+
/** Opt-in: `CARTETHYIA_CURSOR_WIRE_TRACE=1`. Shape only; never payload text. */
|
|
119
|
+
export function wireTraceEnabled(): boolean {
|
|
120
|
+
return process.env.CARTETHYIA_CURSOR_WIRE_TRACE === "1";
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** The "went quiet" error, naming the last checkpoint (donor 0.2.34). */
|
|
124
|
+
export function cursorQuietError(checkpoint: CursorCheckpointState | undefined): GatewayError {
|
|
125
|
+
if (checkpoint === undefined || checkpoint.index === 0) {
|
|
126
|
+
return new GatewayError(
|
|
127
|
+
"platform_unavailable",
|
|
128
|
+
502,
|
|
129
|
+
"Cursor Run went quiet before any checkpoint (request {requestId}). The upstream came back empty-handed — retry 🌙",
|
|
130
|
+
{ providerId: "cursor", kind: "quiet" },
|
|
131
|
+
"upstream",
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
return new GatewayError(
|
|
135
|
+
"platform_unavailable",
|
|
136
|
+
502,
|
|
137
|
+
`Cursor Run went quiet at checkpoint ${checkpoint.index} (step checkpoint #${checkpoint.step}, ` +
|
|
138
|
+
`${checkpoint.streak} in a row; preceded by ${checkpoint.kinds.join(", ") || "no frames"}) (request {requestId}). ` +
|
|
139
|
+
"The upstream came back empty-handed — retry 🌙",
|
|
140
|
+
{ providerId: "cursor", kind: "quiet", checkpointIndex: checkpoint.index },
|
|
141
|
+
"upstream",
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** Structured failure kinds, mapped once from an upstream error. */
|
|
146
|
+
export type CursorFailureKind =
|
|
147
|
+
| "model_not_found"
|
|
148
|
+
| "conversation_rejected"
|
|
149
|
+
| "resource_exhausted"
|
|
150
|
+
| "not_implemented"
|
|
151
|
+
| "h2_blocked"
|
|
152
|
+
| "quiet"
|
|
153
|
+
| "transport"
|
|
154
|
+
| "authentication"
|
|
155
|
+
| "fatal";
|
|
156
|
+
|
|
157
|
+
export interface CursorFailureClassification {
|
|
158
|
+
readonly kind: CursorFailureKind;
|
|
159
|
+
readonly retryable: boolean;
|
|
160
|
+
readonly fallbackH1: boolean;
|
|
161
|
+
readonly rotateConversation: boolean;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/** Donor patterns (cursor.ts L381-L405). */
|
|
165
|
+
const MODEL_NOT_FOUND = /^(?:Connect error not_found:|gRPC error 5:)|AI Model Not Found/i;
|
|
166
|
+
const RESOURCE_EXHAUSTED = /resource.?exhausted/i;
|
|
167
|
+
const CONVERSATION_REJECTED = /conversation.*(rejected|conflict|stale|not found)|store conflict/i;
|
|
168
|
+
const NOT_IMPLEMENTED = /not implemented by this client$/i;
|
|
169
|
+
|
|
170
|
+
export function classifyCursorFailure(error: unknown, transport: "http2" | "http1"): CursorFailureClassification {
|
|
171
|
+
if (error instanceof GatewayError) {
|
|
172
|
+
if (error.code === "authentication_failed") {
|
|
173
|
+
return { kind: "authentication", retryable: false, fallbackH1: false, rotateConversation: false };
|
|
174
|
+
}
|
|
175
|
+
if (error.code === "quota_exceeded" || RESOURCE_EXHAUSTED.test(error.message)) {
|
|
176
|
+
return { kind: "resource_exhausted", retryable: false, fallbackH1: false, rotateConversation: false };
|
|
177
|
+
}
|
|
178
|
+
if (MODEL_NOT_FOUND.test(error.message)) {
|
|
179
|
+
return { kind: "model_not_found", retryable: false, fallbackH1: false, rotateConversation: false };
|
|
180
|
+
}
|
|
181
|
+
if (error.code === "invalid_request") {
|
|
182
|
+
return { kind: "fatal", retryable: false, fallbackH1: false, rotateConversation: false };
|
|
183
|
+
}
|
|
184
|
+
if (error.details["kind"] === "quiet") {
|
|
185
|
+
return { kind: "quiet", retryable: true, fallbackH1: false, rotateConversation: false };
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
189
|
+
if (MODEL_NOT_FOUND.test(message)) {
|
|
190
|
+
return { kind: "model_not_found", retryable: false, fallbackH1: false, rotateConversation: false };
|
|
191
|
+
}
|
|
192
|
+
if (CONVERSATION_REJECTED.test(message)) {
|
|
193
|
+
return { kind: "conversation_rejected", retryable: true, fallbackH1: false, rotateConversation: true };
|
|
194
|
+
}
|
|
195
|
+
if (RESOURCE_EXHAUSTED.test(message)) {
|
|
196
|
+
return { kind: "resource_exhausted", retryable: false, fallbackH1: false, rotateConversation: false };
|
|
197
|
+
}
|
|
198
|
+
if (NOT_IMPLEMENTED.test(message)) {
|
|
199
|
+
return { kind: "not_implemented", retryable: false, fallbackH1: false, rotateConversation: false };
|
|
200
|
+
}
|
|
201
|
+
if (/http\/?2|nghttp2|alpn|rst_stream|goaway|protocol error/i.test(message) && transport === "http2") {
|
|
202
|
+
return { kind: "h2_blocked", retryable: true, fallbackH1: true, rotateConversation: false };
|
|
203
|
+
}
|
|
204
|
+
if (/quiet|no server message|heartbeat/i.test(message)) {
|
|
205
|
+
return { kind: "quiet", retryable: true, fallbackH1: false, rotateConversation: false };
|
|
206
|
+
}
|
|
207
|
+
return { kind: "transport", retryable: true, fallbackH1: false, rotateConversation: false };
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* One settlement for a Run's transport: `end` / `error` / `timeout`,
|
|
212
|
+
* whichever arrives first. Late events are no-ops (donor settleH2).
|
|
213
|
+
*/
|
|
214
|
+
export function createRunCompletion() {
|
|
215
|
+
let settle!: (reason: "end" | "error" | "timeout") => void;
|
|
216
|
+
const promise = new Promise<"end" | "error" | "timeout">((resolve) => {
|
|
217
|
+
settle = resolve;
|
|
218
|
+
});
|
|
219
|
+
let settled = false;
|
|
220
|
+
return {
|
|
221
|
+
promise,
|
|
222
|
+
settle(reason: "end" | "error" | "timeout"): boolean {
|
|
223
|
+
if (settled) return false;
|
|
224
|
+
settled = true;
|
|
225
|
+
settle(reason);
|
|
226
|
+
return true;
|
|
227
|
+
},
|
|
228
|
+
get settled(): boolean {
|
|
229
|
+
return settled;
|
|
230
|
+
},
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Drains dispatched exec handlers on BOTH exits (donor L1198-L1233).
|
|
236
|
+
*
|
|
237
|
+
* A dispatch can spawn another, so the set is re-checked rather than awaited
|
|
238
|
+
* as one snapshot. The wait is bounded by the abort signal: an exec handler
|
|
239
|
+
* has no cancellation contract, and a hung tool must not hold the terminal
|
|
240
|
+
* event hostage after the caller gave up.
|
|
241
|
+
*/
|
|
242
|
+
export async function drainInFlightDispatches(
|
|
243
|
+
dispatches: Set<Promise<void>>,
|
|
244
|
+
signal?: AbortSignal,
|
|
245
|
+
): Promise<void> {
|
|
246
|
+
if (dispatches.size === 0 || signal?.aborted === true) return;
|
|
247
|
+
if (signal === undefined) {
|
|
248
|
+
while (dispatches.size > 0) await Promise.all(dispatches);
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
let onAbort: (() => void) | undefined;
|
|
252
|
+
const aborted = new Promise<void>((resolve) => {
|
|
253
|
+
onAbort = resolve;
|
|
254
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
255
|
+
});
|
|
256
|
+
try {
|
|
257
|
+
while (dispatches.size > 0 && !signal.aborted) {
|
|
258
|
+
await Promise.race([Promise.all(dispatches), aborted]);
|
|
259
|
+
}
|
|
260
|
+
} finally {
|
|
261
|
+
if (onAbort !== undefined) signal.removeEventListener("abort", onAbort);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* A step ends when the server's own tool count covers the calls this client
|
|
267
|
+
* dispatched (donor/magpie 0.2.35): waiting out the heartbeats after that is
|
|
268
|
+
* what made a finished tool round look hung. Server-owned calls are already
|
|
269
|
+
* in the count and are never re-sent.
|
|
270
|
+
*/
|
|
271
|
+
export function reconcileToolCount(
|
|
272
|
+
checkpoint: CursorCheckpointState,
|
|
273
|
+
serverToolCount: number,
|
|
274
|
+
): { endStep: boolean; resend: string[] } {
|
|
275
|
+
const resend = [...checkpoint.pendingCallIds].filter((id) => !checkpoint.completedCallIds.has(id));
|
|
276
|
+
const expected = checkpoint.expectedCallCount ?? serverToolCount;
|
|
277
|
+
const counted = serverToolCount + checkpoint.serverOwnedCount;
|
|
278
|
+
return { endStep: counted >= Math.max(checkpoint.dispatchedCount, expected), resend };
|
|
279
|
+
}
|