cartethyia 2.4.5 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +4 -1
- package/README.md +63 -13
- package/dist/dashboard/assets/AccountCooldown-ChpdzxRH.js.gz +0 -0
- package/dist/dashboard/assets/BannedPage-9igfctfZ.js +1 -0
- package/dist/dashboard/assets/BannedPage-9igfctfZ.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolDetailPage-BzGevuql.js → CliToolDetailPage-ZRr2K2sF.js} +1 -1
- package/dist/dashboard/assets/CliToolDetailPage-ZRr2K2sF.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolsPage-BaRM_7We.js → CliToolsPage-D3xqZbN-.js} +1 -1
- package/dist/dashboard/assets/CliToolsPage-D3xqZbN-.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-CVnQPBBz.js +1 -0
- package/dist/dashboard/assets/CombosPage-CVnQPBBz.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-u6W1UXSc.js +1 -0
- package/dist/dashboard/assets/ConsoleLogPage-u6W1UXSc.js.gz +0 -0
- package/dist/dashboard/assets/{CustomizationPage-DqUGcoH0.js → CustomizationPage-CTTpCb25.js} +1 -1
- package/dist/dashboard/assets/CustomizationPage-CTTpCb25.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-C5XpWlvy.js +1 -0
- package/dist/dashboard/assets/HealthEventsModal-C5XpWlvy.js.gz +0 -0
- package/dist/dashboard/assets/{LoginPage-DbhTGfhb.js → LoginPage-DHoB3Agn.js} +1 -1
- package/dist/dashboard/assets/LoginPage-DHoB3Agn.js.gz +0 -0
- package/dist/dashboard/assets/ModelPicker-oQ-SDXp8.js +1 -0
- package/dist/dashboard/assets/ModelPicker-oQ-SDXp8.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-BrY4SgIt.js +1 -0
- package/dist/dashboard/assets/OverviewPage-BrY4SgIt.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-D9VSt7T_.js +3 -0
- package/dist/dashboard/assets/ProviderDetailPage-D9VSt7T_.js.gz +0 -0
- package/dist/dashboard/assets/ProviderIcon-CmfzxpVM.js +1 -0
- package/dist/dashboard/assets/ProviderIcon-CmfzxpVM.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-Df77V5I5.js +1 -0
- package/dist/dashboard/assets/ProvidersPage-Df77V5I5.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-CDfxP7AC.js +7 -0
- package/dist/dashboard/assets/ProxyPage-CDfxP7AC.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-CY4o2t4p.js +1 -0
- package/dist/dashboard/assets/QuotaPage-CY4o2t4p.js.gz +0 -0
- package/dist/dashboard/assets/{SettingsPage-YxRml8AT.js → SettingsPage-CvjZxHE4.js} +1 -1
- package/dist/dashboard/assets/SettingsPage-CvjZxHE4.js.gz +0 -0
- package/dist/dashboard/assets/SetupPage-D3nza7LH.js +1 -0
- package/dist/dashboard/assets/SetupPage-D3nza7LH.js.gz +0 -0
- package/dist/dashboard/assets/{SortableList-Bol1ASmI.js → SortableList-Dtt12WT1.js} +1 -1
- package/dist/dashboard/assets/SortableList-Dtt12WT1.js.gz +0 -0
- package/dist/dashboard/assets/StudioPage-CbK3hXvU.js +31 -0
- package/dist/dashboard/assets/StudioPage-CbK3hXvU.js.gz +0 -0
- package/dist/dashboard/assets/ToolIcon-K1ckcDKg.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-BQlWbWg2.js +56 -0
- package/dist/dashboard/assets/UsagePage-BQlWbWg2.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-BmDbsior.js +1 -0
- package/dist/dashboard/assets/api-keys-BmDbsior.js.gz +0 -0
- package/dist/dashboard/assets/app-BbpSarVV.css +2 -0
- package/dist/dashboard/assets/app-BbpSarVV.css.gz +0 -0
- package/dist/dashboard/assets/{app-BDJWMThu.js → app-C-Y4oZT0.js} +3 -3
- package/dist/dashboard/assets/app-C-Y4oZT0.js.gz +0 -0
- package/dist/dashboard/assets/arrow-left-CpBxkoxI.js.gz +0 -0
- package/dist/dashboard/assets/arrow-right-Dl5yKnp8.js.gz +0 -0
- package/dist/dashboard/assets/assert-BHfaei-X.js +1 -0
- package/dist/dashboard/assets/assert-BHfaei-X.js.gz +0 -0
- package/dist/dashboard/assets/backup-file-gdKIlg8U.js.gz +0 -0
- package/dist/dashboard/assets/{badge-BwmwEOgA.js → badge-DpXT4Ixt.js} +1 -1
- package/dist/dashboard/assets/badge-DpXT4Ixt.js.gz +0 -0
- package/dist/dashboard/assets/{banner-BKBfKTqn.js → banner-CSAAWLJv.js} +1 -1
- package/dist/dashboard/assets/banner-CSAAWLJv.js.gz +0 -0
- package/dist/dashboard/assets/brain-Cwed_72z.js.gz +0 -0
- package/dist/dashboard/assets/{card-BTdbRFFg.js → card-DEhRXXw4.js} +1 -1
- package/dist/dashboard/assets/card-DEhRXXw4.js.gz +0 -0
- package/dist/dashboard/assets/check-D69FzdvC.js.gz +0 -0
- package/dist/dashboard/assets/chevron-right-BXIy5l3h.js.gz +0 -0
- package/dist/dashboard/assets/circle-alert-CntYHV5v.js.gz +0 -0
- package/dist/dashboard/assets/circle-check-BU3v_mFW.js.gz +0 -0
- package/dist/dashboard/assets/cli-tools-B0wBq-RL.js +1 -0
- package/dist/dashboard/assets/cli-tools-B0wBq-RL.js.gz +0 -0
- package/dist/dashboard/assets/clipboard-button-DOo6EU8O.js +1 -0
- package/dist/dashboard/assets/clipboard-button-DOo6EU8O.js.gz +0 -0
- package/dist/dashboard/assets/cog-CdWhsajs.js.gz +0 -0
- package/dist/dashboard/assets/copy-ngmQ9zcZ.js.gz +0 -0
- package/dist/dashboard/assets/{dialog-iocKA3jr.js → dialog-BvQeBMfP.js} +1 -1
- package/dist/dashboard/assets/dialog-BvQeBMfP.js.gz +0 -0
- package/dist/dashboard/assets/dialog-actions-B1axpEg7.js.gz +0 -0
- package/dist/dashboard/assets/download-A0SW9OCW.js.gz +0 -0
- package/dist/dashboard/assets/download-dRFo5_Wu.js.gz +0 -0
- package/dist/dashboard/assets/{drawer-CDKOTZdY.js → drawer-CMwPXwQB.js} +1 -1
- package/dist/dashboard/assets/drawer-CMwPXwQB.js.gz +0 -0
- package/dist/dashboard/assets/entry-D4O2229X.js +1 -0
- package/dist/dashboard/assets/entry-D4O2229X.js.gz +0 -0
- package/dist/dashboard/assets/entry-DKvr5Q9K.js +1 -0
- package/dist/dashboard/assets/entry-DKvr5Q9K.js.gz +0 -0
- package/dist/dashboard/assets/entry-FQCejoDY.js +4 -0
- package/dist/dashboard/assets/entry-FQCejoDY.js.gz +0 -0
- package/dist/dashboard/assets/eye-C_hawfH4.js.gz +0 -0
- package/dist/dashboard/assets/eye-off-Bk5g2luF.js.gz +0 -0
- package/dist/dashboard/assets/fingerprint-pattern-BWuHtbW4.js.gz +0 -0
- package/dist/dashboard/assets/gauge-BbIMy7MB.js.gz +0 -0
- package/dist/dashboard/assets/github-badge-zliLvBwr.js +1 -0
- package/dist/dashboard/assets/github-badge-zliLvBwr.js.gz +0 -0
- package/dist/dashboard/assets/image-BPaouViG.js.gz +0 -0
- package/dist/dashboard/assets/{index-C4hXfAMs.js → index-CXm3FIyt.js} +2 -2
- package/dist/dashboard/assets/index-CXm3FIyt.js.gz +0 -0
- package/dist/dashboard/assets/{inline-DCejSLlA.js → inline-D1IlZZ8H.js} +1 -1
- package/dist/dashboard/assets/inline-D1IlZZ8H.js.gz +0 -0
- package/dist/dashboard/assets/{input-Bn4_kNav.js → input-BpVZdrbc.js} +1 -1
- package/dist/dashboard/assets/input-BpVZdrbc.js.gz +0 -0
- package/dist/dashboard/assets/jsx-runtime-CXUMngI_.js.gz +0 -0
- package/dist/dashboard/assets/key-round-UkavsGxp.js.gz +0 -0
- package/dist/dashboard/assets/layers-2-glBbjl.js.gz +0 -0
- package/dist/dashboard/assets/{layout-Bv-NxqZ5.js → layout-BioLW7gN.js} +1 -1
- package/dist/dashboard/assets/layout-BioLW7gN.js.gz +0 -0
- package/dist/dashboard/assets/{list-row-BLA0sldD.js → list-row-CwVcGAIc.js} +1 -1
- package/dist/dashboard/assets/list-row-CwVcGAIc.js.gz +0 -0
- package/dist/dashboard/assets/{live-YlifPYox.js → live-yfEJFF9T.js} +1 -1
- package/dist/dashboard/assets/live-yfEJFF9T.js.gz +0 -0
- package/dist/dashboard/assets/log-in-BmRjy60c.js.gz +0 -0
- package/dist/dashboard/assets/message-circle-CCxGwRvv.js.gz +0 -0
- package/dist/dashboard/assets/model-bans-CT-TxSNX.js +1 -0
- package/dist/dashboard/assets/model-bans-CT-TxSNX.js.gz +0 -0
- package/dist/dashboard/assets/mutation-BkoLM5Ba.js +1 -0
- package/dist/dashboard/assets/mutation-BkoLM5Ba.js.gz +0 -0
- package/dist/dashboard/assets/numbers-CFL4_Dqc.js.gz +0 -0
- package/dist/dashboard/assets/pencil-768ysRAr.js.gz +0 -0
- package/dist/dashboard/assets/plus-DIdX_g-x.js.gz +0 -0
- package/dist/dashboard/assets/power-off-Yjw9GMJh.js +1 -0
- package/dist/dashboard/assets/power-off-Yjw9GMJh.js.gz +0 -0
- package/dist/dashboard/assets/privacy-mode-GM8bej85.js +1 -0
- package/dist/dashboard/assets/privacy-mode-GM8bej85.js.gz +0 -0
- package/dist/dashboard/assets/providers/agentrouter.svg.gz +0 -0
- package/dist/dashboard/assets/providers/aihubmix.svg.gz +0 -0
- package/dist/dashboard/assets/providers/alibaba-coding-plan.svg.gz +0 -0
- package/dist/dashboard/assets/providers/alibaba.svg.gz +0 -0
- package/dist/dashboard/assets/providers/antigravity.svg.gz +0 -0
- package/dist/dashboard/assets/providers/bai.svg.gz +0 -0
- package/dist/dashboard/assets/providers/blackbox.svg.gz +0 -0
- package/dist/dashboard/assets/providers/claude-code.svg.gz +0 -0
- package/dist/dashboard/assets/providers/cursor.webp +0 -0
- package/dist/dashboard/assets/providers/exa.svg.gz +0 -0
- package/dist/dashboard/assets/providers/gmi-cloud.svg.gz +0 -0
- package/dist/dashboard/assets/providers/inferhub.svg.gz +0 -0
- package/dist/dashboard/assets/providers/muse.svg.gz +0 -0
- package/dist/dashboard/assets/providers/nous-research.svg.gz +0 -0
- package/dist/dashboard/assets/providers/openai-light.svg.gz +0 -0
- package/dist/dashboard/assets/providers/parallel.svg.gz +0 -0
- package/dist/dashboard/assets/providers/tokenharbor.svg.gz +0 -0
- package/dist/dashboard/assets/providers/trae.webp +0 -0
- package/dist/dashboard/assets/providers/zai-coding-plan.svg.gz +0 -0
- package/dist/dashboard/assets/{query-keys-ROf-g1Wi.js → query-keys-D7wX2vM5.js} +1 -1
- package/dist/dashboard/assets/query-keys-D7wX2vM5.js.gz +0 -0
- package/dist/dashboard/assets/quota-DZGyp0N5.js.gz +0 -0
- package/dist/dashboard/assets/refresh-cw-BqJFk4sU.js.gz +0 -0
- package/dist/dashboard/assets/rotate-ccw-CufcqITi.js.gz +0 -0
- package/dist/dashboard/assets/route-CNSNj9Zm.js.gz +0 -0
- package/dist/dashboard/assets/routing-Cs6jl49L.js +1 -0
- package/dist/dashboard/assets/routing-Cs6jl49L.js.gz +0 -0
- package/dist/dashboard/assets/scaling-nWE4Cx8f.js.gz +0 -0
- package/dist/dashboard/assets/search-JOKus9rb.js.gz +0 -0
- package/dist/dashboard/assets/select-C0Rer3gF.js +41 -0
- package/dist/dashboard/assets/select-C0Rer3gF.js.gz +0 -0
- package/dist/dashboard/assets/settings-CfcTcOyU.js +1 -0
- package/dist/dashboard/assets/settings-CfcTcOyU.js.gz +0 -0
- package/dist/dashboard/assets/shield-check-DVS0Foqh.js.gz +0 -0
- package/dist/dashboard/assets/sparkles-BRaRUxAU.js.gz +0 -0
- package/dist/dashboard/assets/{stack-C1kTcOmP.js → stack-C9_uUjaA.js} +1 -1
- package/dist/dashboard/assets/stack-C9_uUjaA.js.gz +0 -0
- package/dist/dashboard/assets/{state-DHKdg-pm.js → state-DA0B95UQ.js} +1 -1
- package/dist/dashboard/assets/state-DA0B95UQ.js.gz +0 -0
- package/dist/dashboard/assets/stream-retry-5QnVuSSo.js.gz +0 -0
- package/dist/dashboard/assets/{switch-DIDeAiz9.js → switch-Cittxy-x.js} +1 -1
- package/dist/dashboard/assets/switch-Cittxy-x.js.gz +0 -0
- package/dist/dashboard/assets/terminal-Bd4ee8TA.js +1 -0
- package/dist/dashboard/assets/terminal-Bd4ee8TA.js.gz +0 -0
- package/dist/dashboard/assets/toast-CNt_jzO4.js +2 -0
- package/dist/dashboard/assets/toast-CNt_jzO4.js.gz +0 -0
- package/dist/dashboard/assets/tokens-DDxMbSRL.js.gz +0 -0
- package/dist/dashboard/assets/trash-Cppn5c3R.js.gz +0 -0
- package/dist/dashboard/assets/use-clipboard-B0Sy03fE.js.gz +0 -0
- package/dist/dashboard/assets/use-presence-BSZEBx7U.js.gz +0 -0
- package/dist/dashboard/assets/use-reduced-motion-C4B0R6Sz.js.gz +0 -0
- package/dist/dashboard/assets/use-timeout-BwcBBRrT.js.gz +0 -0
- package/dist/dashboard/assets/{useMutation-DVmwOKU-.js → useMutation-BQKMcJ1H.js} +1 -1
- package/dist/dashboard/assets/useMutation-BQKMcJ1H.js.gz +0 -0
- package/dist/dashboard/assets/wrench-B6E1oEuY.js.gz +0 -0
- package/dist/dashboard/index.html +1 -1
- package/dist/dashboard/index.html.gz +0 -0
- package/dist/dashboard/precompressed-assets.json +122 -0
- package/migrations/full-postgres/0013_add_providers_tenant_id_index.sql +22 -0
- package/migrations/full-postgres/0014_add_provider_accounts_tenant_id_index.sql +21 -0
- package/migrations/full-postgres/0015_add_payload_stages.sql +18 -0
- package/migrations/full-postgres/0016_add_share_link_slug.sql +20 -0
- package/migrations/full-postgres/0017_add_models_pro_only.sql +18 -0
- package/migrations/full-postgres/0018_add_accounts_quota_plan.sql +18 -0
- package/migrations/full-postgres/0019_add_models_free_tier.sql +15 -0
- package/migrations/lite-sqlite3/0000_baseline.sql +4 -2
- package/migrations/lite-sqlite3/0013_add_providers_tenant_id_index.sql +4 -0
- package/migrations/lite-sqlite3/0014_add_provider_accounts_tenant_id_index.sql +4 -0
- package/migrations/lite-sqlite3/0015_add_payload_stages.sql +18 -0
- package/migrations/lite-sqlite3/0016_add_share_link_slug.sql +20 -0
- package/migrations/lite-sqlite3/0017_add_models_pro_only.sql +18 -0
- package/migrations/lite-sqlite3/0018_add_accounts_quota_plan.sql +18 -0
- package/migrations/lite-sqlite3/0019_add_models_free_tier.sql +15 -0
- package/package.json +26 -30
- package/scripts/cli/{carte-launcher.cjs → bin/carte.cjs} +8 -8
- package/scripts/cli/{cli-launcher.cjs → bin/cartethyia.cjs} +167 -134
- package/scripts/cli/command/admin.ts +65 -0
- package/scripts/cli/command/console-menu.ts +246 -0
- package/scripts/cli/{doctor.ts → command/doctor.ts} +257 -258
- package/scripts/cli/{setup.ts → command/setup.ts} +8 -12
- package/scripts/cli/command/start.ts +77 -0
- package/scripts/cli/command/tray.ts +165 -0
- package/scripts/cli/command/update.ts +23 -0
- package/scripts/cli/{runtime-state.ts → gateway/drain-token.ts} +78 -78
- package/scripts/cli/gateway/instance-lock.ts +105 -0
- package/scripts/cli/gateway/spawn.ts +173 -0
- package/scripts/cli/gateway/stop.ts +51 -0
- package/scripts/cli/main.ts +94 -0
- package/scripts/cli/support/bun.ts +27 -0
- package/scripts/cli/support/env.ts +99 -0
- package/scripts/cli/support/platform.ts +64 -0
- package/scripts/cli/support/services.ts +88 -0
- package/scripts/cli/support/startup-signal.ts +23 -0
- package/scripts/cli/{version.ts → support/version.ts} +1 -1
- package/scripts/cli/tray/hidden-window.vbs +28 -0
- package/scripts/cli/{tray.ts → tray/index.ts} +3 -3
- package/scripts/cli/{tray.ps1 → tray/windows.ps1} +107 -98
- package/scripts/cli/ui/background.ts +110 -0
- package/src/boot/app.ts +23 -7
- package/src/boot/dependencies.ts +55 -13
- package/src/boot/elysia-typebox-bridge.ts +37 -0
- package/src/boot/lifecycle.ts +2 -0
- package/src/config/cooldowns.ts +138 -138
- package/src/config/providers.ts +31 -5
- package/src/config/runtime.ts +19 -3
- package/src/config/spec.ts +20 -0
- package/src/console/api-keys/contracts.ts +414 -391
- package/src/console/api-keys/routes.ts +880 -870
- package/src/console/auth/account-reset-store.ts +14 -0
- package/src/console/auth/account-reset.ts +3 -2
- package/src/console/auth/setup-service.ts +6 -3
- package/src/console/backup/routes.ts +8 -0
- package/src/console/cli-tools/contracts.ts +6 -6
- package/src/console/console-router.ts +5 -3
- package/src/console/dashboard-assets.ts +81 -11
- package/src/console/domain-registration.ts +9 -2
- package/src/console/observability/contracts.ts +475 -423
- package/src/console/observability/routes.ts +31 -6
- package/src/console/observability/store.ts +1015 -912
- package/src/console/providers/catalog/contracts.ts +31 -0
- package/src/console/providers/catalog/provider-operations.ts +8 -0
- package/src/console/providers/catalog/public-model-store.ts +91 -60
- package/src/console/providers/catalog/store.ts +1532 -1521
- package/src/console/providers/oauth/callback-listener.ts +18 -3
- package/src/console/providers/oauth/routes.ts +38 -15
- package/src/console/quota/account-quota-tenant.ts +9 -6
- package/src/console/quota/refresh.ts +19 -0
- package/src/console/response-compression.ts +69 -2
- package/src/console/routing/pools/probe-verdict.ts +72 -0
- package/src/console/share/share-router.ts +7 -4
- package/src/console/share/share-stats.ts +12 -5
- package/src/console/shared/read-through-cache.ts +133 -7
- package/src/console/updates/repository-updates.ts +39 -34
- package/src/console/updates/routes.ts +162 -4
- package/src/main.ts +3 -0
- package/src/network/http2-bidi.ts +85 -0
- package/src/network/outbound-fetch.ts +87 -24
- package/src/network/pool/agent.ts +478 -460
- package/src/network/pool/resolver.ts +1104 -544
- package/src/network/pool-health-machine.ts +12 -5
- package/src/observability/event-log.ts +29 -8
- package/src/observability/metrics.ts +40 -13
- package/src/observability/payload-capture.ts +344 -324
- package/src/observability/runtime-metrics.ts +7 -0
- package/src/persistence/db-pg.ts +9 -3
- package/src/persistence/db-sqlite.ts +497 -495
- package/src/persistence/health-event-flush.ts +137 -0
- package/src/persistence/migrate.ts +181 -129
- package/src/persistence/schema.ts +1360 -1257
- package/src/persistence/settings-revision.ts +25 -0
- package/src/persistence/shared-cache.ts +217 -0
- package/src/persistence/stores/share-store.ts +556 -528
- package/src/persistence/usage-flush.ts +61 -1
- package/src/protocol/canonical-model.ts +23 -1
- package/src/protocol/response/chat.ts +143 -10
- package/src/protocol/response/codex.ts +9 -75
- package/src/protocol/response/omp-tool-call-markup.ts +221 -0
- package/src/protocol/tool-identity.ts +43 -9
- package/src/providers/authentication/jwt-validator.ts +130 -187
- package/src/providers/authentication/oauth-flow-store.ts +16 -9
- package/src/providers/default-registry.ts +177 -1
- package/src/providers/discovery/catalog/catalog-format.ts +20 -0
- package/src/providers/discovery/models-dev-catalog.ts +39 -22
- package/src/providers/discovery/openai-model-discovery.ts +39 -3
- package/src/providers/discovery/openrouter-free-roster.ts +98 -0
- package/src/providers/discovery/probe-wire.ts +29 -5
- package/src/providers/discovery/probing-service.ts +1022 -1001
- package/src/providers/integrations/buddy/buddy-catalog-shared.ts +42 -2
- package/src/providers/integrations/buddy/buddy-chat-shared.ts +913 -914
- package/src/providers/integrations/buddy/buddy-cli-headers.ts +88 -0
- package/src/providers/integrations/buddy/buddy-conversation-headers.ts +22 -5
- package/src/providers/integrations/buddy/buddy-discovery-shared.ts +33 -1
- package/src/providers/integrations/buddy/buddy-identity.ts +46 -0
- package/src/providers/integrations/buddy/buddy-oauth-shared.ts +37 -1
- package/src/providers/integrations/buddy/buddy-usage-store.ts +40 -5
- package/src/providers/integrations/buddy/codebuddy/cn-provider.ts +4 -0
- package/src/providers/integrations/buddy/codebuddy/provider.ts +180 -175
- package/src/providers/integrations/buddy/codebuddy/shared.ts +115 -73
- package/src/providers/integrations/buddy/workbuddy/cn-provider.ts +5 -2
- package/src/providers/integrations/buddy/workbuddy/provider.ts +206 -204
- package/src/providers/integrations/buddy/workbuddy/quota.ts +172 -159
- package/src/providers/integrations/buddy/workbuddy/shared.ts +200 -171
- package/src/providers/integrations/claude/claude.ts +606 -582
- package/src/providers/integrations/cline/cline-quota.ts +18 -16
- package/src/providers/integrations/cline/cline.ts +539 -468
- package/src/providers/integrations/codex/codex.ts +5 -8
- package/src/providers/integrations/commandcode/commandcode.ts +675 -541
- package/src/providers/integrations/commandcode/discovery.ts +116 -0
- package/src/providers/integrations/commandcode/quota.ts +160 -0
- package/src/providers/integrations/cursor/cursor-auth.ts +418 -0
- package/src/providers/integrations/cursor/cursor-catalog.ts +368 -0
- package/src/providers/integrations/cursor/cursor-http2.ts +300 -0
- package/src/providers/integrations/cursor/cursor-identity.ts +92 -0
- package/src/providers/integrations/cursor/cursor-request.ts +471 -0
- package/src/providers/integrations/cursor/cursor-session-manager.ts +281 -0
- package/src/providers/integrations/cursor/cursor-shared.ts +108 -0
- package/src/providers/integrations/cursor/cursor.ts +960 -0
- package/src/providers/integrations/cursor/protocol/framing.ts +129 -0
- package/src/providers/integrations/cursor/protocol/messages.ts +2355 -0
- package/src/providers/integrations/cursor/protocol/struct.ts +305 -0
- package/src/providers/integrations/factory/factory-oauth.ts +7 -11
- package/src/providers/integrations/github/github.ts +4 -4
- package/src/providers/integrations/hermes.ts +79 -8
- package/src/providers/integrations/kilo/kilo-discovery.ts +10 -1
- package/src/providers/integrations/kilo/kilo.ts +153 -146
- package/src/providers/integrations/opencode/opencode-go-quota.ts +130 -0
- package/src/providers/integrations/opencode/provider.ts +532 -499
- package/src/providers/integrations/openrouter/provider.ts +139 -281
- package/src/providers/integrations/qoder/qoder-catalog.ts +450 -0
- package/src/providers/integrations/qoder/qoder-oauth.ts +310 -263
- package/src/providers/integrations/qoder/qoder-realm.ts +106 -0
- package/src/providers/integrations/qoder/qoder-virtual.ts +77 -0
- package/src/providers/integrations/qoder/qoder.ts +865 -730
- package/src/providers/integrations/shared/install-id.ts +29 -2
- package/src/providers/integrations/trae/trae-catalog.ts +190 -0
- package/src/providers/integrations/trae/trae-headers.ts +149 -0
- package/src/providers/integrations/trae/trae-oauth.ts +773 -0
- package/src/providers/integrations/trae/trae-protocol.ts +400 -0
- package/src/providers/integrations/trae/trae-realm.ts +220 -0
- package/src/providers/integrations/trae/trae.ts +458 -0
- package/src/providers/operations/account-health-service.ts +107 -10
- package/src/providers/operations/client-versions.ts +143 -6
- package/src/providers/operations/model-catalog-cache.ts +18 -3
- package/src/providers/operations/provider-catalog-seeder.ts +95 -86
- package/src/providers/operations/provider-catalog-service.ts +76 -0
- package/src/providers/operations/provider-credential-service.ts +46 -7
- package/src/providers/provider-contracts.ts +76 -0
- package/src/providers/provider-metadata.ts +78 -6
- package/src/providers/provider-model-definition.ts +373 -349
- package/src/security/api-key-auth.ts +126 -8
- package/src/transport/contracts.ts +3 -0
- package/src/transport/dispatch/attempt-finalize.ts +39 -2
- package/src/transport/dispatch/attempt-loop.ts +530 -513
- package/src/transport/dispatch/empty-stream.ts +17 -26
- package/src/transport/dispatch/generation.ts +88 -21
- package/src/transport/dispatch/leases.ts +99 -2
- package/src/transport/dispatch/proxy-request.ts +60 -3
- package/src/transport/dispatch/streaming/finalize.ts +16 -1
- package/src/transport/dispatch/streaming/stream.ts +22 -2
- package/src/transport/dispatch/upstream.ts +19 -1
- package/src/transport/failure-policy.ts +20 -0
- package/src/transport/guards/auth-guard.ts +26 -1
- package/src/transport/guards/body-policy.ts +5 -4
- package/src/transport/request/preparer.ts +103 -0
- package/src/transport/request/state.ts +14 -0
- package/src/transport/routing/plan-gate.ts +45 -0
- package/src/transport/routing/route-catalog.ts +799 -738
- package/src/transport/routing/route-model.ts +52 -1
- package/src/transport/routing/router.ts +51 -0
- package/src/transport/surface/chat/encode.ts +18 -31
- package/src/transport/surface/messages/stream.ts +3 -2
- package/src/transport/translation/tool-repair.ts +37 -16
- package/src/workers/account-identity-sweep.ts +14 -12
- package/src/workers/daily-checkin.ts +62 -0
- package/src/workers/quota-refresh-sweep.ts +27 -2
- package/dist/dashboard/assets/BannedPage-DUrXpp-X.js +0 -1
- package/dist/dashboard/assets/CombosPage-B7ETkfVH.js +0 -1
- package/dist/dashboard/assets/ConsoleLogPage-AisQzCI1.js +0 -1
- package/dist/dashboard/assets/HealthEventsModal-BNflDm_f.js +0 -1
- package/dist/dashboard/assets/ModelPicker-CAiPR5Vd.js +0 -1
- package/dist/dashboard/assets/OverviewPage-C3NIzsDx.js +0 -1
- package/dist/dashboard/assets/ProviderDetailPage-BK8i5B5X.js +0 -3
- package/dist/dashboard/assets/ProviderIcon-paOCq8I0.js +0 -1
- package/dist/dashboard/assets/ProvidersPage-CvCVgm1e.js +0 -1
- package/dist/dashboard/assets/ProxyPage-DjabkuzP.js +0 -7
- package/dist/dashboard/assets/QuotaPage-Dxe0B0iu.js +0 -1
- package/dist/dashboard/assets/SetupPage-BaD4P_jW.js +0 -1
- package/dist/dashboard/assets/StudioPage-CLP4le3U.js +0 -31
- package/dist/dashboard/assets/UsagePage-Djes-Epy.js +0 -56
- package/dist/dashboard/assets/api-keys-CjIGQhe7.js +0 -1
- package/dist/dashboard/assets/app-Cjgo4-Lf.css +0 -2
- package/dist/dashboard/assets/assert-Bwh6DJTK.js +0 -1
- package/dist/dashboard/assets/cli-tools-DccaOfhX.js +0 -1
- package/dist/dashboard/assets/clipboard-button-DQmMikNQ.js +0 -1
- package/dist/dashboard/assets/entry-Cpi0rph1.js +0 -4
- package/dist/dashboard/assets/entry-Dm3PpDCO.js +0 -1
- package/dist/dashboard/assets/entry-keNDZ_Wz.js +0 -1
- package/dist/dashboard/assets/github-badge-BuAJr8oU.js +0 -1
- package/dist/dashboard/assets/model-bans-CVvHZ-qz.js +0 -1
- package/dist/dashboard/assets/mutation-BIizFZav.js +0 -1
- package/dist/dashboard/assets/privacy-mode-CQjbZ3xk.js +0 -1
- package/dist/dashboard/assets/routing-jntw9-Ui.js +0 -1
- package/dist/dashboard/assets/select-CGUuTQmj.js +0 -41
- package/dist/dashboard/assets/settings-BAS6QbmJ.js +0 -1
- package/dist/dashboard/assets/sliders-horizontal-ClG7dBeJ.js +0 -1
- package/dist/dashboard/assets/terminal-C9mZ4Ugm.js +0 -1
- package/dist/dashboard/assets/toast-DKAdP2mo.js +0 -2
- package/scripts/cli/cli.ts +0 -771
- package/scripts/operations/probe-egress.ts +0 -335
- package/scripts/operations/restart.ts +0 -120
- package/scripts/shared/env.ts +0 -269
- /package/{tray-assets → scripts/cli/tray/assets}/cartethyia.ico +0 -0
- /package/{tray-assets → scripts/cli/tray/assets}/cartethyia.png +0 -0
- /package/scripts/cli/{menu.ts → ui/prompt.ts} +0 -0
- /package/scripts/cli/{wordmark.ts → ui/wordmark.ts} +0 -0
|
@@ -80,11 +80,30 @@ async function writeLifetimeDeltas(
|
|
|
80
80
|
const FLUSH_STATEMENT_MAX_KEYS = 500;
|
|
81
81
|
/** Max distinct keys retained across windows; oldest dropped first. */
|
|
82
82
|
const FLUSH_PENDING_MAX_KEYS = 5000;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Consecutive fully-failed flushes before the writer backs off.
|
|
86
|
+
*
|
|
87
|
+
* A database that is down stays down: retrying every `intervalMs` means a
|
|
88
|
+
* write attempt per second against a backend that is already overloaded, which
|
|
89
|
+
* is load the operator cannot distinguish from real traffic and which delays
|
|
90
|
+
* recovery. After this many failures the buffer stops probing and waits out a
|
|
91
|
+
* cooldown instead.
|
|
92
|
+
*/
|
|
93
|
+
const FLUSH_FAILURE_STREAK_LIMIT = 3;
|
|
94
|
+
/** How long a failing writer waits before probing again. */
|
|
95
|
+
const FLUSH_BACKOFF_MS = 15_000;
|
|
83
96
|
export class UsageFlushBuffer {
|
|
84
97
|
private readonly pending = new Map<string, number>();
|
|
85
98
|
private timer: ReturnType<typeof setTimeout> | undefined;
|
|
86
99
|
private flushing = false;
|
|
87
100
|
private stopped = false;
|
|
101
|
+
/** Consecutive flushes that failed outright; drives the backoff below. */
|
|
102
|
+
private failureStreak = 0;
|
|
103
|
+
/** Wall-clock time before which no flush is attempted. */
|
|
104
|
+
private backoffUntil = 0;
|
|
105
|
+
/** Deltas abandoned because the buffer could not persist them. */
|
|
106
|
+
private droppedDelta = 0;
|
|
88
107
|
|
|
89
108
|
constructor(
|
|
90
109
|
private readonly db: CartethyiaDatabase,
|
|
@@ -104,6 +123,19 @@ export class UsageFlushBuffer {
|
|
|
104
123
|
}
|
|
105
124
|
}
|
|
106
125
|
|
|
126
|
+
/**
|
|
127
|
+
* Deltas recorded but not yet persisted, for one key.
|
|
128
|
+
*
|
|
129
|
+
* Exposed so a reader of `lifetime_tokens_consumed` can add what is in
|
|
130
|
+
* flight to what Postgres last stored. Without it such a reader saw a value
|
|
131
|
+
* that was behind by up to one flush interval, and — because the flush is
|
|
132
|
+
* coalescing by design — behind by the largest amount exactly when the key
|
|
133
|
+
* was busiest.
|
|
134
|
+
*/
|
|
135
|
+
pendingFor(apiKeyId: string): number {
|
|
136
|
+
return this.pending.get(apiKeyId) ?? 0;
|
|
137
|
+
}
|
|
138
|
+
|
|
107
139
|
/**
|
|
108
140
|
* Writes everything accumulated so far, in bounded statements of at most
|
|
109
141
|
* 500 keys each. One giant statement grows the SQL string with the
|
|
@@ -116,9 +148,14 @@ export class UsageFlushBuffer {
|
|
|
116
148
|
*/
|
|
117
149
|
async flush(): Promise<void> {
|
|
118
150
|
if (this.flushing || this.pending.size === 0) return;
|
|
151
|
+
// Backing off during a sustained outage: without this the writer probed a
|
|
152
|
+
// failing database on every tick, adding write load to the very overload
|
|
153
|
+
// that caused the failure.
|
|
154
|
+
if (Date.now() < this.backoffUntil) return;
|
|
119
155
|
this.flushing = true;
|
|
120
156
|
const batch = new Map(this.pending);
|
|
121
157
|
this.pending.clear();
|
|
158
|
+
let failed = 0;
|
|
122
159
|
try {
|
|
123
160
|
const entries = [...batch];
|
|
124
161
|
for (let offset = 0; offset < entries.length; offset += FLUSH_STATEMENT_MAX_KEYS) {
|
|
@@ -134,15 +171,38 @@ export class UsageFlushBuffer {
|
|
|
134
171
|
try {
|
|
135
172
|
await this.write(this.db, apiKeyId, delta);
|
|
136
173
|
} catch {
|
|
137
|
-
|
|
174
|
+
// A transient failure retries, but only while the buffer is not
|
|
175
|
+
// itself saturated: an outage that outlasts the cap must drop
|
|
176
|
+
// accounting rather than grow the pending map without bound.
|
|
177
|
+
if (this.pending.size < FLUSH_PENDING_MAX_KEYS) {
|
|
178
|
+
this.pending.set(apiKeyId, (this.pending.get(apiKeyId) ?? 0) + delta);
|
|
179
|
+
} else {
|
|
180
|
+
this.droppedDelta += delta;
|
|
181
|
+
}
|
|
182
|
+
failed += 1;
|
|
138
183
|
}
|
|
139
184
|
}
|
|
140
185
|
}
|
|
141
186
|
} finally {
|
|
187
|
+
if (failed > 0) {
|
|
188
|
+
this.failureStreak += 1;
|
|
189
|
+
if (this.failureStreak >= FLUSH_FAILURE_STREAK_LIMIT) {
|
|
190
|
+
this.backoffUntil = Date.now() + FLUSH_BACKOFF_MS;
|
|
191
|
+
this.failureStreak = 0;
|
|
192
|
+
}
|
|
193
|
+
} else {
|
|
194
|
+
this.failureStreak = 0;
|
|
195
|
+
this.backoffUntil = 0;
|
|
196
|
+
}
|
|
142
197
|
this.flushing = false;
|
|
143
198
|
}
|
|
144
199
|
}
|
|
145
200
|
|
|
201
|
+
/** Total usage delta abandoned after sustained write failure. */
|
|
202
|
+
get droppedTotal(): number {
|
|
203
|
+
return this.droppedDelta;
|
|
204
|
+
}
|
|
205
|
+
|
|
146
206
|
/** Stops the timer. Pass `{ flush: true }` to persist what is still pending. */
|
|
147
207
|
async stop(options?: { flush?: boolean }): Promise<void> {
|
|
148
208
|
this.stopped = true;
|
|
@@ -33,7 +33,16 @@ export type WireFamily = (typeof WIRE_FAMILIES)[number];
|
|
|
33
33
|
* the console's Elysia body schema and its validator both project this list, so
|
|
34
34
|
* a new kind cannot reach one and miss the other.
|
|
35
35
|
*/
|
|
36
|
-
export const SERVICE_KINDS = [
|
|
36
|
+
export const SERVICE_KINDS = [
|
|
37
|
+
"llm",
|
|
38
|
+
"systemone",
|
|
39
|
+
"websearch",
|
|
40
|
+
"imageGeneration",
|
|
41
|
+
"videoGeneration",
|
|
42
|
+
"embedding",
|
|
43
|
+
"audioTranscription",
|
|
44
|
+
"audioSpeech",
|
|
45
|
+
] as const;
|
|
37
46
|
|
|
38
47
|
/** Provider service kind selected by a model's catalog row. */
|
|
39
48
|
export type ServiceKind = (typeof SERVICE_KINDS)[number];
|
|
@@ -354,6 +363,19 @@ export interface CanonicalTerminalEvent {
|
|
|
354
363
|
readonly response_id?: string;
|
|
355
364
|
/** Event timestamp in milliseconds since epoch (optional for profiling). */
|
|
356
365
|
readonly timestamp?: number;
|
|
366
|
+
/**
|
|
367
|
+
* Set by an adapter whose turn is not finished: the stream ended because the
|
|
368
|
+
* model asked for a tool the client must answer, and the provider's
|
|
369
|
+
* authoritative usage arrives with the *later* request that continues the
|
|
370
|
+
* same upstream turn.
|
|
371
|
+
*
|
|
372
|
+
* The settle path reads this to skip charging estimated tokens for the
|
|
373
|
+
* interim response — otherwise one logical turn bills an input/output
|
|
374
|
+
* estimate on the tool-call response and the real usage on the continuation,
|
|
375
|
+
* which double-counts the turn. Only a provider that genuinely spans client
|
|
376
|
+
* requests with one upstream turn sets it.
|
|
377
|
+
*/
|
|
378
|
+
readonly usage_deferred?: true;
|
|
357
379
|
}
|
|
358
380
|
|
|
359
381
|
/** An ordered canonical stream event; sequence numbers must increase monotonically. */
|
|
@@ -11,6 +11,11 @@ import { usageFromProvider, readReasoningText } from "../../providers/provider-u
|
|
|
11
11
|
import { createToolEmitLedger } from "../tool-identity";
|
|
12
12
|
import { MimoThinkSplitter, stripThinkingTags } from "../../providers/integrations/xiaomi-mimo/think-stream";
|
|
13
13
|
import { parseDsmlToolCalls } from "./dsml";
|
|
14
|
+
import {
|
|
15
|
+
OmpToolCallMarkupParser,
|
|
16
|
+
parseOmpToolCallMarkup,
|
|
17
|
+
type OmpTextToolCall,
|
|
18
|
+
} from "./omp-tool-call-markup";
|
|
14
19
|
|
|
15
20
|
|
|
16
21
|
export function mapChatStopReason(
|
|
@@ -43,14 +48,23 @@ export function parseChatResponseToEvents(
|
|
|
43
48
|
const choices = (json["choices"] as Array<Record<string, unknown>>) ?? [];
|
|
44
49
|
const first = choices[0] as Record<string, unknown> | undefined;
|
|
45
50
|
const message = first?.["message"] as Record<string, unknown> | undefined;
|
|
51
|
+
const declaredToolNames = declaredToolNamesForRequest(request);
|
|
52
|
+
const callableToolNames = callableToolNamesForRequest(request);
|
|
53
|
+
let recoveredToolCalls = 0;
|
|
46
54
|
if (message) {
|
|
47
55
|
const reasoningRaw = readReasoningText(message);
|
|
56
|
+
const reasoningMarkup =
|
|
57
|
+
typeof reasoningRaw === "string" && (request.tools?.length ?? 0) > 0
|
|
58
|
+
? parseOmpToolCallMarkup(reasoningRaw, declaredToolNames)
|
|
59
|
+
: undefined;
|
|
48
60
|
// hy4-preview-f (and other buddy reasoning models) sometimes wraps its own
|
|
49
61
|
// reasoning in `<think>…</think>` inside `reasoning_content`, which then
|
|
50
62
|
// reaches the client as literal tag bytes. The field itself is the stream
|
|
51
63
|
// for reasoning here, so the tags cannot carry structure — strip them.
|
|
52
64
|
const reasoning =
|
|
53
|
-
typeof reasoningRaw === "string"
|
|
65
|
+
typeof reasoningRaw === "string"
|
|
66
|
+
? stripThinkingTags(reasoningMarkup?.text ?? reasoningRaw)
|
|
67
|
+
: reasoningRaw;
|
|
54
68
|
if (typeof reasoning === "string" && reasoning.length > 0) {
|
|
55
69
|
events.push({
|
|
56
70
|
type: "content_delta",
|
|
@@ -62,8 +76,16 @@ export function parseChatResponseToEvents(
|
|
|
62
76
|
});
|
|
63
77
|
}
|
|
64
78
|
const content = message["content"] as string | null;
|
|
65
|
-
const
|
|
66
|
-
|
|
79
|
+
const recoveredMarkup =
|
|
80
|
+
typeof content === "string" && (request.tools?.length ?? 0) > 0
|
|
81
|
+
? parseOmpToolCallMarkup(content, declaredToolNames)
|
|
82
|
+
: undefined;
|
|
83
|
+
const contentWithoutMarkup = recoveredMarkup?.text ?? content;
|
|
84
|
+
const recovered =
|
|
85
|
+
typeof contentWithoutMarkup === "string"
|
|
86
|
+
? parseDsmlToolCalls(contentWithoutMarkup)
|
|
87
|
+
: undefined;
|
|
88
|
+
const visible = recovered === undefined ? contentWithoutMarkup : recovered.text;
|
|
67
89
|
if (typeof visible === "string" && visible.includes("<think")) {
|
|
68
90
|
const think = new MimoThinkSplitter();
|
|
69
91
|
const split = think.push(visible);
|
|
@@ -104,9 +126,39 @@ export function parseChatResponseToEvents(
|
|
|
104
126
|
});
|
|
105
127
|
}
|
|
106
128
|
const structuredCalls = message["tool_calls"];
|
|
107
|
-
const
|
|
129
|
+
const hasStructuredCalls =
|
|
130
|
+
Array.isArray(structuredCalls) &&
|
|
131
|
+
structuredCalls.some((candidate) => {
|
|
132
|
+
if (typeof candidate !== "object" || candidate === null) return false;
|
|
133
|
+
const record = candidate as Record<string, unknown>;
|
|
134
|
+
const fn =
|
|
135
|
+
typeof record["function"] === "object" && record["function"] !== null
|
|
136
|
+
? (record["function"] as Record<string, unknown>)
|
|
137
|
+
: undefined;
|
|
138
|
+
const callId = typeof record["id"] === "string" && record["id"].length > 0;
|
|
139
|
+
const name =
|
|
140
|
+
typeof fn?.["name"] === "string" && fn["name"].length > 0
|
|
141
|
+
? fn["name"]
|
|
142
|
+
: typeof record["name"] === "string"
|
|
143
|
+
? record["name"]
|
|
144
|
+
: undefined;
|
|
145
|
+
return callId && name !== undefined && name.length > 0;
|
|
146
|
+
});
|
|
147
|
+
const toolCalls = hasStructuredCalls
|
|
108
148
|
? structuredCalls
|
|
109
|
-
:
|
|
149
|
+
: recoveredMarkup?.calls.some((call) => callableToolNames.has(call.name))
|
|
150
|
+
? recoveredMarkup.calls
|
|
151
|
+
.filter((call) => callableToolNames.has(call.name))
|
|
152
|
+
.map((call) => ({
|
|
153
|
+
id: call.id,
|
|
154
|
+
function: { name: call.name, arguments: call.arguments },
|
|
155
|
+
}))
|
|
156
|
+
: recovered?.calls ?? [];
|
|
157
|
+
if (!hasStructuredCalls && recoveredMarkup !== undefined) {
|
|
158
|
+
recoveredToolCalls = recoveredMarkup.calls.filter((call) =>
|
|
159
|
+
callableToolNames.has(call.name),
|
|
160
|
+
).length;
|
|
161
|
+
}
|
|
110
162
|
for (const tc of toolCalls) {
|
|
111
163
|
if (typeof tc !== "object" || tc === null) continue;
|
|
112
164
|
const record = tc as Record<string, unknown>;
|
|
@@ -166,7 +218,7 @@ export function parseChatResponseToEvents(
|
|
|
166
218
|
sequenceNumber: events.length + 1,
|
|
167
219
|
responseId: id,
|
|
168
220
|
state: "complete",
|
|
169
|
-
stopReason: chatJsonStopReason,
|
|
221
|
+
stopReason: recoveredToolCalls > 0 ? "tool_use" : chatJsonStopReason,
|
|
170
222
|
providerStopReason: chatJsonProviderStopReason,
|
|
171
223
|
usage: usageRecord,
|
|
172
224
|
}),
|
|
@@ -194,9 +246,17 @@ export async function* decodeChatSseStream(
|
|
|
194
246
|
const lastToolNameByIndex = new Map<number, string>();
|
|
195
247
|
let lastSeenToolId: string | undefined;
|
|
196
248
|
let lastSeenToolName: string | undefined;
|
|
249
|
+
let structuredToolCallEmitted = false;
|
|
250
|
+
const recoveredMarkupCalls: OmpTextToolCall[] = [];
|
|
197
251
|
// Single emission ledger: one entry per logical call.
|
|
198
252
|
const ledger = createToolEmitLedger();
|
|
199
253
|
const think = new MimoThinkSplitter();
|
|
254
|
+
const declaredToolNames = declaredToolNamesForRequest(request);
|
|
255
|
+
const callableToolNames = callableToolNamesForRequest(request);
|
|
256
|
+
const contentMarkup =
|
|
257
|
+
(request.tools?.length ?? 0) > 0 ? new OmpToolCallMarkupParser(declaredToolNames) : undefined;
|
|
258
|
+
const reasoningMarkup =
|
|
259
|
+
(request.tools?.length ?? 0) > 0 ? new OmpToolCallMarkupParser(declaredToolNames) : undefined;
|
|
200
260
|
yield {
|
|
201
261
|
type: "response_start",
|
|
202
262
|
sequence_number: seq++,
|
|
@@ -240,8 +300,10 @@ export async function* decodeChatSseStream(
|
|
|
240
300
|
// text/thinking blocks.
|
|
241
301
|
if (typeof delta?.["content"] === "string" && delta["content"].length > 0) {
|
|
242
302
|
const raw = delta["content"] as string;
|
|
243
|
-
const
|
|
244
|
-
|
|
303
|
+
const markup = contentMarkup?.push(raw);
|
|
304
|
+
if (markup !== undefined) recoveredMarkupCalls.push(...markup.calls);
|
|
305
|
+
const recovered = parseDsmlToolCalls(markup?.text ?? raw);
|
|
306
|
+
const visible = recovered?.text ?? markup?.text ?? raw;
|
|
245
307
|
const split = think.push(visible);
|
|
246
308
|
if (split.reasoning.length > 0) {
|
|
247
309
|
yield {
|
|
@@ -270,12 +332,16 @@ export async function* decodeChatSseStream(
|
|
|
270
332
|
}
|
|
271
333
|
}
|
|
272
334
|
const reasoningRaw = delta ? readReasoningText(delta) : undefined;
|
|
335
|
+
const reasoningMarkupResult =
|
|
336
|
+
typeof reasoningRaw === "string" ? reasoningMarkup?.push(reasoningRaw) : undefined;
|
|
273
337
|
// Strip stray `<think>` tags the upstream double-wrapped inside its own
|
|
274
338
|
// reasoning stream (buddy's hy4-preview-f leaks them per-delta). Done
|
|
275
339
|
// per delta rather than once at the end because an open-ended tag that
|
|
276
340
|
// spans multiple deltas is a splitter job, not this one.
|
|
277
341
|
const reasoningText =
|
|
278
|
-
typeof reasoningRaw === "string"
|
|
342
|
+
typeof reasoningRaw === "string"
|
|
343
|
+
? stripThinkingTags(reasoningMarkupResult?.text ?? reasoningRaw)
|
|
344
|
+
: reasoningRaw;
|
|
279
345
|
if (typeof reasoningText === "string" && reasoningText.length > 0) {
|
|
280
346
|
yield {
|
|
281
347
|
type: "content_delta",
|
|
@@ -353,6 +419,7 @@ export async function* decodeChatSseStream(
|
|
|
353
419
|
ledger.isDuplicateDefinition(id, fragName, args);
|
|
354
420
|
if (!duplicate) {
|
|
355
421
|
if (establishedNew) ledger.claimFirstEmission(id, fragName, args);
|
|
422
|
+
structuredToolCallEmitted = true;
|
|
356
423
|
yield {
|
|
357
424
|
type: "tool_call_delta",
|
|
358
425
|
sequence_number: seq++,
|
|
@@ -390,6 +457,38 @@ export async function* decodeChatSseStream(
|
|
|
390
457
|
);
|
|
391
458
|
}
|
|
392
459
|
}
|
|
460
|
+
const contentMarkupTail = contentMarkup?.flush();
|
|
461
|
+
if (contentMarkupTail !== undefined) {
|
|
462
|
+
recoveredMarkupCalls.push(...contentMarkupTail.calls);
|
|
463
|
+
if (contentMarkupTail.text.length > 0) {
|
|
464
|
+
const split = think.push(contentMarkupTail.text);
|
|
465
|
+
if (split.reasoning.length > 0) {
|
|
466
|
+
yield {
|
|
467
|
+
type: "content_delta",
|
|
468
|
+
sequence_number: seq++,
|
|
469
|
+
content: { kind: "reasoning", payload: null, summary: split.reasoning },
|
|
470
|
+
} as CanonicalEvent;
|
|
471
|
+
}
|
|
472
|
+
if (split.text.length > 0) {
|
|
473
|
+
yield {
|
|
474
|
+
type: "content_delta",
|
|
475
|
+
sequence_number: seq++,
|
|
476
|
+
content: { kind: "text", text: split.text },
|
|
477
|
+
} as CanonicalEvent;
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
const reasoningMarkupTail = reasoningMarkup?.flush();
|
|
482
|
+
if (reasoningMarkupTail?.text) {
|
|
483
|
+
const reasoningText = stripThinkingTags(reasoningMarkupTail.text);
|
|
484
|
+
if (reasoningText.length > 0) {
|
|
485
|
+
yield {
|
|
486
|
+
type: "content_delta",
|
|
487
|
+
sequence_number: seq++,
|
|
488
|
+
content: { kind: "reasoning", payload: null, summary: reasoningText },
|
|
489
|
+
} as CanonicalEvent;
|
|
490
|
+
}
|
|
491
|
+
}
|
|
393
492
|
const rest = think.flush();
|
|
394
493
|
if (rest.reasoning.length > 0) {
|
|
395
494
|
yield {
|
|
@@ -405,6 +504,20 @@ export async function* decodeChatSseStream(
|
|
|
405
504
|
content: { kind: "text", text: rest.text },
|
|
406
505
|
} as CanonicalEvent;
|
|
407
506
|
}
|
|
507
|
+
let recoveredMarkupToolCallsEmitted = 0;
|
|
508
|
+
if (!structuredToolCallEmitted) {
|
|
509
|
+
for (const call of recoveredMarkupCalls) {
|
|
510
|
+
if (!callableToolNames.has(call.name)) continue;
|
|
511
|
+
yield {
|
|
512
|
+
type: "tool_call_delta",
|
|
513
|
+
sequence_number: seq++,
|
|
514
|
+
call_id: call.id,
|
|
515
|
+
name: call.name,
|
|
516
|
+
arguments_delta: call.arguments,
|
|
517
|
+
} as CanonicalEvent;
|
|
518
|
+
recoveredMarkupToolCallsEmitted += 1;
|
|
519
|
+
}
|
|
520
|
+
}
|
|
408
521
|
const chatStopReason = mapChatStopReason(finishReason);
|
|
409
522
|
const chatProviderStopReason =
|
|
410
523
|
typeof finishReason === "string" ? finishReason : undefined;
|
|
@@ -423,8 +536,28 @@ export async function* decodeChatSseStream(
|
|
|
423
536
|
// `stop`. `chatFinishReason(undefined)` falls back to `stop`, so the client
|
|
424
537
|
// saw a completed turn while telemetry recorded a 502 upstream transport
|
|
425
538
|
// failure — the failure this terminal already carries was invisible to it.
|
|
426
|
-
stopReason:
|
|
539
|
+
stopReason:
|
|
540
|
+
truncated && signal?.aborted !== true
|
|
541
|
+
? "error"
|
|
542
|
+
: !structuredToolCallEmitted && recoveredMarkupToolCallsEmitted > 0
|
|
543
|
+
? "tool_use"
|
|
544
|
+
: chatStopReason,
|
|
427
545
|
providerStopReason: chatProviderStopReason,
|
|
428
546
|
usage: usageFromProvider(rawUsage),
|
|
429
547
|
});
|
|
430
548
|
}
|
|
549
|
+
|
|
550
|
+
function declaredToolNamesForRequest(request: CanonicalRequest): ReadonlySet<string> {
|
|
551
|
+
return new Set((request.tools ?? []).map((tool) => tool.name));
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
function callableToolNamesForRequest(request: CanonicalRequest): ReadonlySet<string> {
|
|
555
|
+
const declared = declaredToolNamesForRequest(request);
|
|
556
|
+
const choice = request.tool_choice;
|
|
557
|
+
if (choice === "none") return new Set();
|
|
558
|
+
if (choice === undefined || typeof choice === "string") return declared;
|
|
559
|
+
if (choice.type === "tool" || choice.type === "custom") {
|
|
560
|
+
return declared.has(choice.name) ? new Set([choice.name]) : new Set();
|
|
561
|
+
}
|
|
562
|
+
return new Set(choice.names.filter((name) => declared.has(name)));
|
|
563
|
+
}
|
|
@@ -281,9 +281,7 @@ export class CodexStreamFrameProcessor {
|
|
|
281
281
|
// output_index routing + `output_item.added` registration.
|
|
282
282
|
readonly #openItems = new Map<string, OpenItemEntry>();
|
|
283
283
|
readonly #openItemsByOutputIndex = new Map<number, OpenItemEntry>();
|
|
284
|
-
|
|
285
|
-
// future reconciliation of streamed arguments against authoritative ones.
|
|
286
|
-
readonly #toolCallArgs = new Map<string, string[]>();
|
|
284
|
+
|
|
287
285
|
// Whitespace-loop guard: a model stuck emitting only whitespace tool-call
|
|
288
286
|
// argument deltas would otherwise hang the dispatch loop forever.
|
|
289
287
|
readonly #whitespaceDeltaTracking = new Map<
|
|
@@ -427,9 +425,6 @@ export class CodexStreamFrameProcessor {
|
|
|
427
425
|
: undefined;
|
|
428
426
|
const key = itemId ?? callId;
|
|
429
427
|
this.#checkWhitespaceLoopGuard(key, json["delta"] as string);
|
|
430
|
-
const chunks = this.#toolCallArgs.get(key) ?? [];
|
|
431
|
-
chunks.push(json["delta"] as string);
|
|
432
|
-
this.#toolCallArgs.set(key, chunks);
|
|
433
428
|
events.push({
|
|
434
429
|
type: "tool_call_delta",
|
|
435
430
|
sequence_number: this.#seq++,
|
|
@@ -443,14 +438,7 @@ export class CodexStreamFrameProcessor {
|
|
|
443
438
|
typeof json["delta"] === "string"
|
|
444
439
|
) {
|
|
445
440
|
const { callId, name } = this.#resolveToolIdentity(json);
|
|
446
|
-
|
|
447
|
-
typeof json["item_id"] === "string"
|
|
448
|
-
? (json["item_id"] as string)
|
|
449
|
-
: undefined;
|
|
450
|
-
const key = itemId ?? callId;
|
|
451
|
-
const chunks = this.#toolCallArgs.get(key) ?? [];
|
|
452
|
-
chunks.push(json["delta"] as string);
|
|
453
|
-
this.#toolCallArgs.set(key, chunks);
|
|
441
|
+
|
|
454
442
|
events.push({
|
|
455
443
|
type: "tool_call_delta",
|
|
456
444
|
sequence_number: this.#seq++,
|
|
@@ -505,37 +493,13 @@ export class CodexStreamFrameProcessor {
|
|
|
505
493
|
},
|
|
506
494
|
});
|
|
507
495
|
} else if (t === "response.function_call_arguments.done") {
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
const itemId =
|
|
512
|
-
typeof json["item_id"] === "string"
|
|
513
|
-
? (json["item_id"] as string)
|
|
514
|
-
: undefined;
|
|
515
|
-
let key: string | undefined;
|
|
516
|
-
if (itemId !== undefined) key = itemId;
|
|
517
|
-
else if (rawCallId !== undefined) key = decodeCodexToolCallId(rawCallId);
|
|
518
|
-
else {
|
|
519
|
-
const outputIndex = readOutputIndex(json);
|
|
520
|
-
if (outputIndex !== undefined) key = `idx_${outputIndex}`;
|
|
521
|
-
}
|
|
522
|
-
const finalArgs = (json as Record<string, unknown>)["arguments"];
|
|
523
|
-
if (typeof finalArgs === "string" && key !== undefined) {
|
|
524
|
-
this.#toolCallArgs.set(key, [finalArgs]);
|
|
525
|
-
}
|
|
496
|
+
// The arguments themselves are emitted by the delta events above; the
|
|
497
|
+
// `.done` event only marks the call complete.
|
|
498
|
+
|
|
526
499
|
this.hasToolCall = true;
|
|
527
500
|
} else if (t === "response.custom_tool_call_input.done") {
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
? (json["item_id"] as string)
|
|
531
|
-
: undefined;
|
|
532
|
-
const rawCallId = (json["call_id"] as string | undefined) ?? itemId;
|
|
533
|
-
let key: string | undefined;
|
|
534
|
-
if (itemId !== undefined) key = itemId;
|
|
535
|
-
else if (rawCallId !== undefined) key = decodeCodexToolCallId(rawCallId);
|
|
536
|
-
const finalInput = (json as Record<string, unknown>)["input"];
|
|
537
|
-
if (typeof finalInput === "string" && key !== undefined)
|
|
538
|
-
this.#toolCallArgs.set(key, [finalInput]);
|
|
501
|
+
// As above: the input arrives as deltas, so `.done` only completes it.
|
|
502
|
+
|
|
539
503
|
this.hasToolCall = true;
|
|
540
504
|
} else if (t === "response.output_item.done") {
|
|
541
505
|
const item = json["item"];
|
|
@@ -598,43 +562,13 @@ export class CodexStreamFrameProcessor {
|
|
|
598
562
|
}
|
|
599
563
|
if (outputItem["type"] === "function_call") {
|
|
600
564
|
this.hasToolCall = true;
|
|
601
|
-
|
|
602
|
-
typeof outputItem["call_id"] === "string"
|
|
603
|
-
? (outputItem["call_id"] as string)
|
|
604
|
-
: undefined;
|
|
605
|
-
const decodedCallId =
|
|
606
|
-
callIdRaw !== undefined
|
|
607
|
-
? decodeCodexToolCallId(callIdRaw)
|
|
608
|
-
: undefined;
|
|
609
|
-
const authoritative =
|
|
610
|
-
typeof outputItem["arguments"] === "string"
|
|
611
|
-
? (outputItem["arguments"] as string)
|
|
612
|
-
: undefined;
|
|
613
|
-
if (authoritative !== undefined && decodedCallId !== undefined) {
|
|
614
|
-
const key = outputItemId ?? decodedCallId;
|
|
615
|
-
this.#toolCallArgs.set(key, [authoritative]);
|
|
616
|
-
}
|
|
565
|
+
|
|
617
566
|
if (outputItemId !== undefined) this.#openItems.delete(outputItemId);
|
|
618
567
|
if (outputIndex !== undefined)
|
|
619
568
|
this.#openItemsByOutputIndex.delete(outputIndex);
|
|
620
569
|
} else if (outputItem["type"] === "custom_tool_call") {
|
|
621
570
|
this.hasToolCall = true;
|
|
622
|
-
|
|
623
|
-
typeof outputItem["call_id"] === "string"
|
|
624
|
-
? (outputItem["call_id"] as string)
|
|
625
|
-
: undefined;
|
|
626
|
-
const decodedCallId =
|
|
627
|
-
callIdRaw !== undefined
|
|
628
|
-
? decodeCodexToolCallId(callIdRaw)
|
|
629
|
-
: undefined;
|
|
630
|
-
const authoritative =
|
|
631
|
-
typeof outputItem["input"] === "string"
|
|
632
|
-
? (outputItem["input"] as string)
|
|
633
|
-
: undefined;
|
|
634
|
-
if (authoritative !== undefined && decodedCallId !== undefined) {
|
|
635
|
-
const key = outputItemId ?? decodedCallId;
|
|
636
|
-
this.#toolCallArgs.set(key, [authoritative]);
|
|
637
|
-
}
|
|
571
|
+
|
|
638
572
|
if (outputItemId !== undefined) this.#openItems.delete(outputItemId);
|
|
639
573
|
if (outputIndex !== undefined)
|
|
640
574
|
this.#openItemsByOutputIndex.delete(outputIndex);
|