cartethyia 2.7.4 → 2.7.6
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 +5 -0
- package/README.md +18 -6
- package/dist/dashboard/assets/BannedPage-B1N603cM.js +1 -0
- package/dist/dashboard/assets/BannedPage-B1N603cM.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-9q4_4NoK.js +3 -0
- package/dist/dashboard/assets/CliToolDetailPage-9q4_4NoK.js.gz +0 -0
- package/dist/dashboard/assets/CliToolsPage-DwshGtK-.js +1 -0
- package/dist/dashboard/assets/CliToolsPage-DwshGtK-.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-D762MIkp.js +1 -0
- package/dist/dashboard/assets/CombosPage-D762MIkp.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-KRYjHZsX.js +1 -0
- package/dist/dashboard/assets/ConsoleLogPage-KRYjHZsX.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-DXsxk5uM.js +1 -0
- package/dist/dashboard/assets/CustomizationPage-DXsxk5uM.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-BEjAjD4F.js +1 -0
- package/dist/dashboard/assets/HealthEventsModal-BEjAjD4F.js.gz +0 -0
- package/dist/dashboard/assets/InjectSystemPromptPanel-d5v3K_Yc.js +899 -0
- package/dist/dashboard/assets/InjectSystemPromptPanel-d5v3K_Yc.js.gz +0 -0
- package/dist/dashboard/assets/LoginPage-Ccv4tjNS.js +1 -0
- package/dist/dashboard/assets/LoginPage-Ccv4tjNS.js.gz +0 -0
- package/dist/dashboard/assets/{ModelPicker-BMS2t8BA.js → ModelPicker-C6WlpU5E.js} +1 -1
- package/dist/dashboard/assets/ModelPicker-C6WlpU5E.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-DN9fYNWA.js +1 -0
- package/dist/dashboard/assets/OverviewPage-DN9fYNWA.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-BY0ieUgz.js +5 -0
- package/dist/dashboard/assets/ProviderDetailPage-BY0ieUgz.js.gz +0 -0
- package/dist/dashboard/assets/ProviderIcon-jjqXJDA3.js +1 -0
- package/dist/dashboard/assets/ProviderIcon-jjqXJDA3.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-DNsbfea2.js +1 -0
- package/dist/dashboard/assets/ProvidersPage-DNsbfea2.js.gz +0 -0
- package/dist/dashboard/assets/{ProxyPage-lsrx7NYg.js → ProxyPage-DxjvUzSD.js} +1 -1
- package/dist/dashboard/assets/ProxyPage-DxjvUzSD.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-OaHScjpX.js +1 -0
- package/dist/dashboard/assets/QuotaPage-OaHScjpX.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-BW4AKj2H.js +1 -0
- package/dist/dashboard/assets/SettingsPage-BW4AKj2H.js.gz +0 -0
- package/dist/dashboard/assets/SetupPage-44BZrxFC.js +1 -0
- package/dist/dashboard/assets/SetupPage-44BZrxFC.js.gz +0 -0
- package/dist/dashboard/assets/{StudioPage-Cq5tW7hW.js → StudioPage-DRPavCdU.js} +4 -4
- package/dist/dashboard/assets/StudioPage-DRPavCdU.js.gz +0 -0
- package/dist/dashboard/assets/ToolIcon-CfeLA-Z_.js +1 -0
- package/dist/dashboard/assets/ToolIcon-CfeLA-Z_.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-B7M7LvMM.js +56 -0
- package/dist/dashboard/assets/UsagePage-B7M7LvMM.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-KvX83mRe.js +1 -0
- package/dist/dashboard/assets/api-keys-KvX83mRe.js.gz +0 -0
- package/dist/dashboard/assets/app-DRnQLhsc.js +9 -0
- package/dist/dashboard/assets/app-DRnQLhsc.js.gz +0 -0
- package/dist/dashboard/assets/app-mweRcsKp.css +2 -0
- package/dist/dashboard/assets/app-mweRcsKp.css.gz +0 -0
- package/dist/dashboard/assets/assert-CidQVoNo.js +1 -0
- package/dist/dashboard/assets/assert-CidQVoNo.js.gz +0 -0
- package/dist/dashboard/assets/{badge-D9CS6dZk.js → badge-BaX2GTrn.js} +1 -1
- package/dist/dashboard/assets/badge-BaX2GTrn.js.gz +0 -0
- package/dist/dashboard/assets/{banner-DympBNq4.js → banner-DFH4Bkte.js} +1 -1
- package/dist/dashboard/assets/banner-DFH4Bkte.js.gz +0 -0
- package/dist/dashboard/assets/brand/login_register.jpg +0 -0
- package/dist/dashboard/assets/brand/login_register_initial.jpg +0 -0
- package/dist/dashboard/assets/brand/og_bansos.jpg +0 -0
- package/dist/dashboard/assets/brand/og_image.jpg +0 -0
- package/dist/dashboard/assets/{card-ibRWU_uT.js → card-CxDw0lK6.js} +1 -1
- package/dist/dashboard/assets/card-CxDw0lK6.js.gz +0 -0
- package/dist/dashboard/assets/chevron-left-Bf6CaTSJ.js +1 -0
- package/dist/dashboard/assets/chevron-left-Bf6CaTSJ.js.gz +0 -0
- package/dist/dashboard/assets/circle-question-mark-Cz4un8sH.js +1 -0
- package/dist/dashboard/assets/circle-question-mark-Cz4un8sH.js.gz +0 -0
- package/dist/dashboard/assets/cli-tools-Bwv5-zGZ.js +1 -0
- package/dist/dashboard/assets/cli-tools-Bwv5-zGZ.js.gz +0 -0
- package/dist/dashboard/assets/{clipboard-button-DJm-9pz7.js → clipboard-button-DfS1ntiq.js} +1 -1
- package/dist/dashboard/assets/clipboard-button-DfS1ntiq.js.gz +0 -0
- package/dist/dashboard/assets/dialog-BK-NRr5u.js +1 -0
- package/dist/dashboard/assets/dialog-BK-NRr5u.js.gz +0 -0
- package/dist/dashboard/assets/{drawer-CqGYeOrd.js → drawer-QqPul6sC.js} +1 -1
- package/dist/dashboard/assets/drawer-QqPul6sC.js.gz +0 -0
- package/dist/dashboard/assets/entry-BMlYJVFj.js +1 -0
- package/dist/dashboard/assets/entry-BMlYJVFj.js.gz +0 -0
- package/dist/dashboard/assets/entry-D6WvojeD.js +4 -0
- package/dist/dashboard/assets/entry-D6WvojeD.js.gz +0 -0
- package/dist/dashboard/assets/entry-Dmx7gBiN.js +1 -0
- package/dist/dashboard/assets/entry-Dmx7gBiN.js.gz +0 -0
- package/dist/dashboard/assets/{formatters-D-MTJxMr.js → formatters-u7JPB5BX.js} +1 -1
- package/dist/dashboard/assets/formatters-u7JPB5BX.js.gz +0 -0
- package/dist/dashboard/assets/house-h9AZlJuT.js +1 -0
- package/dist/dashboard/assets/house-h9AZlJuT.js.gz +0 -0
- package/dist/dashboard/assets/{index-DBNPOfhV.js → index-Ko4dLRnV.js} +2 -2
- package/dist/dashboard/assets/index-Ko4dLRnV.js.gz +0 -0
- package/dist/dashboard/assets/{inline-BT0zntEr.js → inline-CHJQwxLa.js} +1 -1
- package/dist/dashboard/assets/inline-CHJQwxLa.js.gz +0 -0
- package/dist/dashboard/assets/{input-Da2ZViWg.js → input-BlmlizVV.js} +1 -1
- package/dist/dashboard/assets/input-BlmlizVV.js.gz +0 -0
- package/dist/dashboard/assets/{layout-BXNeLw6M.js → layout-Bz93I-yE.js} +1 -1
- package/dist/dashboard/assets/layout-Bz93I-yE.js.gz +0 -0
- package/dist/dashboard/assets/{list-row-BycOrrIV.js → list-row-Ml16KCfu.js} +1 -1
- package/dist/dashboard/assets/list-row-Ml16KCfu.js.gz +0 -0
- package/dist/dashboard/assets/live-7_QpnYGu.js +1 -0
- package/dist/dashboard/assets/live-7_QpnYGu.js.gz +0 -0
- package/dist/dashboard/assets/{mutation-Rr0TIvcj.js → mutation-GU7XdEjz.js} +1 -1
- package/dist/dashboard/assets/mutation-GU7XdEjz.js.gz +0 -0
- package/dist/dashboard/assets/network-CuMDaTzT.js +1 -0
- package/dist/dashboard/assets/network-CuMDaTzT.js.gz +0 -0
- package/dist/dashboard/assets/numbers-CoqpaDMx.js +2 -0
- package/dist/dashboard/assets/numbers-CoqpaDMx.js.gz +0 -0
- package/dist/dashboard/assets/pagination-B5aOoweZ.js +1 -0
- package/dist/dashboard/assets/pagination-B5aOoweZ.js.gz +0 -0
- package/dist/dashboard/assets/providers/zai.webp +0 -0
- package/dist/dashboard/assets/providers/zcode.webp +0 -0
- package/dist/dashboard/assets/{query-keys-BloKtaXJ.js → query-keys-YSoQOUh6.js} +1 -1
- package/dist/dashboard/assets/{query-keys-BloKtaXJ.js.gz → query-keys-YSoQOUh6.js.gz} +0 -0
- package/dist/dashboard/assets/quota-DJ1QlAOq.js +1 -0
- package/dist/dashboard/assets/quota-DJ1QlAOq.js.gz +0 -0
- package/dist/dashboard/assets/routing-fnCsxgNO.js +1 -0
- package/dist/dashboard/assets/routing-fnCsxgNO.js.gz +0 -0
- package/dist/dashboard/assets/{select-BIYIBkMU.js → select-BHqpd-bg.js} +2 -2
- package/dist/dashboard/assets/select-BHqpd-bg.js.gz +0 -0
- package/dist/dashboard/assets/settings-BxslsErn.js +1 -0
- package/dist/dashboard/assets/settings-BxslsErn.js.gz +0 -0
- package/dist/dashboard/assets/shield-BMQNxORh.js +1 -0
- package/dist/dashboard/assets/shield-BMQNxORh.js.gz +0 -0
- package/dist/dashboard/assets/{stack-DfgnFDWY.js → stack-j7cXjgYX.js} +1 -1
- package/dist/dashboard/assets/stack-j7cXjgYX.js.gz +0 -0
- package/dist/dashboard/assets/{state-CkfdYWel.js → state-DHmEnLa2.js} +1 -1
- package/dist/dashboard/assets/state-DHmEnLa2.js.gz +0 -0
- package/dist/dashboard/assets/{switch-SYm237HG.js → switch-DmZcyTIC.js} +1 -1
- package/dist/dashboard/assets/switch-DmZcyTIC.js.gz +0 -0
- package/dist/dashboard/assets/{useMutation-BQqCRNIi.js → useMutation-CbCP4w1o.js} +1 -1
- package/dist/dashboard/assets/useMutation-CbCP4w1o.js.gz +0 -0
- package/dist/dashboard/console-backgrounds.jpg +0 -0
- package/dist/dashboard/console-backgrounds_initial.jpg +0 -0
- package/dist/dashboard/index.html +2 -2
- package/dist/dashboard/index.html.gz +0 -0
- package/dist/dashboard/precompressed-assets.json +60 -59
- package/migrations/full-postgres/0036_system_prompt_presets_refs.sql +10 -0
- package/migrations/full-postgres/0037_share_concurrency_scope.sql +3 -0
- package/migrations/full-postgres/0038_add_accounts_quota_snapshot.sql +7 -0
- package/migrations/full-postgres/0039_add_telemetry_provider_model_indexes.sql +13 -0
- package/migrations/full-postgres/0040_add_models_dialect.sql +3 -0
- package/migrations/full-postgres/0041_add_daily_checkin_ledger_account_fk.sql +7 -0
- package/migrations/full-postgres/0042_add_telemetry_degenerate_stop.sql +14 -0
- package/migrations/lite-sqlite3/0036_system_prompt_presets_refs.sql +14 -0
- package/migrations/lite-sqlite3/0037_share_concurrency_scope.sql +3 -0
- package/migrations/lite-sqlite3/0038_add_accounts_quota_snapshot.sql +4 -0
- package/migrations/lite-sqlite3/0039_add_telemetry_provider_model_indexes.sql +6 -0
- package/migrations/lite-sqlite3/0040_add_models_dialect.sql +3 -0
- package/migrations/lite-sqlite3/0041_add_daily_checkin_ledger_account_fk.sql +26 -0
- package/migrations/lite-sqlite3/0042_add_telemetry_degenerate_stop.sql +12 -0
- package/package.json +4 -6
- package/scripts/cli/update.ts +15 -41
- package/src/boot/app.ts +58 -2
- package/src/boot/dependencies.ts +794 -732
- package/src/boot/lifecycle.ts +11 -12
- package/src/config/cooldowns.ts +34 -55
- package/src/config/network.ts +1 -1
- package/src/config/package-paths.ts +1 -1
- package/src/config/providers.ts +19 -14
- package/src/config/resources.ts +0 -5
- package/src/config/runtime.ts +2 -5
- package/src/config/spec.ts +33 -6
- package/src/config/usage.ts +27 -0
- package/src/console/api-keys/ban-constants.ts +38 -0
- package/src/console/api-keys/contracts.ts +71 -62
- package/src/console/api-keys/routes.ts +56 -24
- package/src/console/auth/access.ts +4 -4
- package/src/console/auth/account-reset.ts +1 -1
- package/src/console/auth/audit-recorder.ts +1 -1
- package/src/console/auth/lockout.ts +7 -3
- package/src/console/auth/row-coercion.ts +57 -12
- package/src/console/auth/session-resolver.ts +1 -1
- package/src/console/auth/session-service.ts +8 -2
- package/src/console/auth/session.ts +37 -31
- package/src/console/auth/setup-service.ts +4 -3
- package/src/console/auth/user-store.ts +1 -1
- package/src/console/backup/9router-map.ts +0 -3
- package/src/console/backup/contracts.ts +9 -9
- package/src/console/backup/export-endpoint.ts +5 -6
- package/src/console/backup/nine-router.ts +1 -7
- package/src/console/backup/routes.ts +29 -33
- package/src/console/backup/service.ts +468 -468
- package/src/console/backup/store.ts +47 -29
- package/src/console/backup/validate.ts +11 -1
- package/src/console/cli-tools/contracts.ts +43 -299
- package/src/console/cli-tools/fs-ops.ts +29 -0
- package/src/console/cli-tools/injectors/claude.ts +3 -3
- package/src/console/cli-tools/injectors/codex.ts +181 -181
- package/src/console/cli-tools/injectors/driver.ts +150 -247
- package/src/console/cli-tools/injectors/kilo.ts +146 -21
- package/src/console/cli-tools/injectors/opencode.ts +176 -153
- package/src/console/cli-tools/routes.ts +7 -1
- package/src/console/cli-tools/service.ts +8 -7
- package/src/console/cli-tools/store.ts +2 -2
- package/src/console/console-router.ts +32 -0
- package/src/console/dashboard-assets.ts +9 -9
- package/src/console/domain-registration.ts +16 -2
- package/src/console/inferhub/client.ts +0 -7
- package/src/console/inferhub/key-store.ts +1 -1
- package/src/console/inferhub/normalize.ts +2 -2
- package/src/console/inferhub/routes.ts +1 -1
- package/src/console/inferhub/types.ts +1 -1
- package/src/console/observability/contracts.ts +7 -1
- package/src/console/observability/live.ts +31 -18
- package/src/console/observability/logs.ts +1 -1
- package/src/console/observability/sse.ts +62 -8
- package/src/console/observability/store.ts +23 -4
- package/src/console/providers/catalog/catalog-projections.ts +1 -1
- package/src/console/providers/catalog/contracts.ts +39 -0
- package/src/console/providers/catalog/model-operations.ts +1 -1
- package/src/console/providers/catalog/provider-operations.ts +95 -17
- package/src/console/providers/catalog/public-model-store.ts +0 -0
- package/src/console/providers/catalog/routes.ts +27 -3
- package/src/console/providers/catalog/store.ts +147 -8
- package/src/console/providers/detail/routes.ts +1 -1
- package/src/console/providers/oauth/callback-listener.ts +100 -18
- package/src/console/providers/oauth/routes.ts +92 -13
- package/src/console/quota/account-quota-global.ts +41 -3
- package/src/console/quota/account-quota-shared.ts +10 -3
- package/src/console/quota/account-quota-tenant.ts +259 -66
- package/src/console/quota/daily-growth-job.ts +308 -0
- package/src/console/quota/native-identity.ts +2 -2
- package/src/console/quota/quota-view.ts +1 -1
- package/src/console/remote-support/routes.ts +1 -1
- package/src/console/remote-support/service.ts +2 -2
- package/src/console/routing/model/contracts.ts +1 -1
- package/src/console/routing/pools/contracts.ts +2 -2
- package/src/console/routing/pools/import-parse.ts +3 -3
- package/src/console/routing/pools/relay-deploy.ts +3 -3
- package/src/console/settings/contracts.ts +29 -2
- package/src/console/settings/store.ts +20 -0
- package/src/console/share/share-router.ts +100 -32
- package/src/console/share/share-stats.ts +11 -13
- package/src/console/share/share-usage.ts +1 -1
- package/src/console/shared/errors.ts +2 -2
- package/src/console/shared/object.ts +18 -0
- package/src/console/shared/read-through-cache.ts +4 -4
- package/src/console/shared/transaction.ts +14 -0
- package/src/console/system-prompt/contracts.ts +89 -12
- package/src/console/system-prompt/routes.ts +98 -45
- package/src/console/updates/apply-update.ts +244 -0
- package/src/console/updates/routes.ts +252 -97
- package/src/main.ts +11 -5
- package/src/network/http2-bidi.ts +1 -1
- package/src/network/ip-utils.ts +298 -0
- package/src/network/outbound-fetch.ts +1 -1
- package/src/network/pool/agent.ts +7 -6
- package/src/network/pool/byte-accounting.ts +1 -1
- package/src/network/pool/resolver.ts +16 -12
- package/src/network/pool/selector.ts +150 -136
- package/src/network/pool-health-machine.ts +1 -1
- package/src/network/rate-limit-verdict.ts +1 -1
- package/src/network/retry.ts +1 -1
- package/src/network/ssrf.ts +20 -71
- package/src/observability/event-log.ts +9 -2
- package/src/observability/event-query.ts +61 -291
- package/src/observability/log-sanitize.ts +1 -1
- package/src/observability/metrics.ts +4 -4
- package/src/observability/payload-capture.ts +6 -5
- package/src/observability/payload-store.ts +1 -1
- package/src/observability/runtime-metrics.ts +1 -1
- package/src/observability/telemetry-buffer.ts +19 -3
- package/src/observability/telemetry-reconciliation.ts +3 -3
- package/src/observability/token-speed.ts +1 -1
- package/src/persistence/connection-url.ts +1 -1
- package/src/persistence/db-handle.ts +1 -1
- package/src/persistence/db-pg.ts +2 -2
- package/src/persistence/db-sqlite.ts +3 -3
- package/src/persistence/dialect.ts +5 -1
- package/src/persistence/readiness.ts +1 -1
- package/src/persistence/redis.ts +1 -1
- package/src/persistence/schema.ts +121 -40
- package/src/persistence/shared-cache.ts +2 -2
- package/src/persistence/sort-order.ts +1 -1
- package/src/persistence/stores/api-key-store.ts +29 -2
- package/src/persistence/stores/share-store.ts +20 -8
- package/src/persistence/system-prompt-store.ts +63 -18
- package/src/persistence/tenant-preferences.ts +1 -1
- package/src/persistence/ttl-cache.ts +3 -3
- package/src/persistence/usage-flush.ts +7 -2
- package/src/protocol/canonical-model.ts +1 -1
- package/src/protocol/codecs.ts +6 -10
- package/src/protocol/dsml.ts +8 -125
- package/src/protocol/image-clamp.ts +2 -2
- package/src/protocol/reasoning-text.ts +1 -1
- package/src/protocol/reasoning.ts +7 -52
- package/src/protocol/registry.ts +50 -15
- package/src/protocol/request/chat.ts +46 -19
- package/src/protocol/request/codex.ts +1 -1
- package/src/protocol/request/messages.ts +46 -14
- package/src/protocol/request/responses.ts +19 -10
- package/src/protocol/response/chat.ts +25 -10
- package/src/protocol/response/codex.ts +1 -1
- package/src/protocol/response/messages.ts +1 -1
- package/src/protocol/response/omp-tool-call-markup.ts +1 -1
- package/src/protocol/response/responses.ts +1 -1
- package/src/protocol/sse-decoder.ts +50 -1
- package/src/protocol/stop-reason.ts +2 -2
- package/src/protocol/think-split.ts +347 -0
- package/src/protocol/translation/attachment-integrity.ts +3 -3
- package/src/protocol/translation/cache-controls.ts +8 -20
- package/src/protocol/translation/capabilities.ts +6 -6
- package/src/protocol/translation/dialect/deepseek.ts +10 -0
- package/src/protocol/translation/dialect/inband.ts +39 -0
- package/src/protocol/translation/dialect/model-thinking.ts +89 -0
- package/src/protocol/translation/dialect/qwen.ts +114 -0
- package/src/protocol/translation/dialect/resolver.ts +83 -0
- package/src/protocol/translation/dialect/rules.ts +93 -0
- package/src/protocol/translation/dialect/thinking-ladder.ts +245 -0
- package/src/protocol/translation/dialect/types.ts +58 -0
- package/src/protocol/translation/quirks.ts +18 -4
- package/src/protocol/translation/thinking.ts +12 -252
- package/src/protocol/translation/tool-id.ts +3 -3
- package/src/protocol/translation/tool-repair.ts +1 -1
- package/src/protocol/upstream/post-json.ts +6 -1
- package/src/providers/adapters/compatible-adapter.ts +71 -4
- package/src/providers/authentication/jwt-validator.ts +0 -5
- package/src/providers/authentication/oauth-client.ts +254 -253
- package/src/providers/authentication/oauth-flow-store.ts +42 -7
- package/src/providers/bundled-discovery-providers.ts +6 -30
- package/src/providers/configured-openai-providers.ts +82 -97
- package/src/providers/default-registry.ts +35 -37
- package/src/providers/discovery/catalog/catalog-format.ts +13 -2
- package/src/providers/discovery/discovery-types.ts +0 -1
- package/src/providers/discovery/openai-model-discovery.ts +2 -2
- package/src/providers/discovery/probe-phases.ts +48 -13
- package/src/providers/discovery/probing-service.ts +6 -8
- package/src/providers/integrations/agentrouter/agentrouter.ts +12 -2
- package/src/providers/integrations/antigravity/antigravity-oauth.ts +0 -1
- package/src/providers/integrations/antigravity/antigravity-protocol.ts +88 -28
- package/src/providers/integrations/antigravity/antigravity-quota.ts +18 -6
- package/src/providers/integrations/antigravity/antigravity.ts +22 -13
- package/src/providers/integrations/buddy/buddy-catalog-shared.ts +1 -1
- package/src/providers/integrations/buddy/buddy-chat-shared.ts +20 -4
- package/src/providers/integrations/buddy/buddy-cli-headers.ts +19 -52
- package/src/providers/integrations/buddy/buddy-identity.ts +86 -0
- package/src/providers/integrations/buddy/buddy-oauth-shared.ts +1 -1
- package/src/providers/integrations/buddy/codebuddy/cn-provider.ts +1 -2
- package/src/providers/integrations/buddy/codebuddy/provider.ts +4 -15
- package/src/providers/integrations/buddy/codebuddy/shared.ts +30 -9
- package/src/providers/integrations/buddy/workbuddy/provider.ts +5 -9
- package/src/providers/integrations/buddy/workbuddy/shared.ts +21 -3
- package/src/providers/integrations/claude/claude-cch.ts +148 -148
- package/src/providers/integrations/claude/fallback-credit.ts +3 -7
- package/src/providers/integrations/cline/cline.ts +1 -1
- package/src/providers/integrations/codex/codex-device-code.ts +101 -101
- package/src/providers/integrations/codex/codex-oauth.ts +0 -1
- package/src/providers/integrations/commandcode/commandcode.ts +75 -115
- package/src/providers/integrations/commandcode/quota.ts +131 -51
- package/src/providers/integrations/cursor/cursor-http2.ts +0 -3
- package/src/providers/integrations/cursor/cursor-quota.ts +2 -6
- package/src/providers/integrations/cursor/cursor-request.ts +0 -87
- package/src/providers/integrations/cursor/cursor-shared.ts +0 -4
- package/src/providers/integrations/cursor/cursor.ts +35 -3
- package/src/providers/integrations/cursor/protocol/framing.ts +0 -56
- package/src/providers/integrations/cursor/protocol/messages.ts +0 -96
- package/src/providers/integrations/factory/factory.ts +9 -9
- package/src/providers/integrations/github/github-discovery.ts +70 -12
- package/src/providers/integrations/github/github-oauth.ts +184 -14
- package/src/providers/integrations/github/github.ts +134 -26
- package/src/providers/integrations/grok/grok-oauth.ts +261 -261
- package/src/providers/integrations/grok/grok-quota-frame.ts +1 -1
- package/src/providers/integrations/grok/grok-quota.ts +16 -7
- package/src/providers/integrations/grok/grok-ratelimit-store.ts +0 -5
- package/src/providers/integrations/grok/grok-ratelimit.ts +3 -0
- package/src/providers/integrations/grok/grok.ts +86 -36
- package/src/providers/integrations/kilo/kilo-oauth.ts +1 -1
- package/src/providers/integrations/kiro/kiro-cache.ts +31 -0
- package/src/providers/integrations/kiro/kiro-catalog.ts +31 -5
- package/src/providers/integrations/kiro/kiro-discovery.ts +47 -43
- package/src/providers/integrations/kiro/kiro-machine-id.ts +6 -3
- package/src/providers/integrations/kiro/kiro-oauth.ts +523 -255
- package/src/providers/integrations/kiro/kiro-quota.ts +35 -47
- package/src/providers/integrations/kiro/kiro-request.ts +8 -11
- package/src/providers/integrations/kiro/kiro-stream.ts +13 -0
- package/src/providers/integrations/kiro/kiro.ts +80 -68
- package/src/providers/integrations/muse/muse-oauth.ts +0 -1
- package/src/providers/integrations/muse/muse-quota.ts +0 -4
- package/src/providers/integrations/opencode/fingerprint.ts +7 -5
- package/src/providers/integrations/opencode/provider.ts +17 -24
- package/src/providers/integrations/qoder/qoder-catalog.ts +94 -11
- package/src/providers/integrations/qoder/qoder-oauth.ts +165 -25
- package/src/providers/integrations/qoder/qoder-quota.ts +48 -6
- package/src/providers/integrations/qoder/qoder-realm.ts +10 -31
- package/src/providers/integrations/qoder/qoder-sse.ts +159 -0
- package/src/providers/integrations/qoder/qoder-umid.ts +21 -25
- package/src/providers/integrations/qoder/qoder-virtual.ts +12 -0
- package/src/providers/integrations/qoder/qoder.ts +422 -71
- package/src/providers/integrations/shared/connect.ts +0 -54
- package/src/providers/integrations/shared/hash.ts +15 -0
- package/src/providers/integrations/shared/session-cache.ts +136 -0
- package/src/providers/integrations/trae/trae-cache.ts +7 -87
- package/src/providers/integrations/trae/trae-catalog.ts +1 -6
- package/src/providers/integrations/trae/trae-protocol.ts +17 -8
- package/src/providers/integrations/trae/trae-realm.ts +0 -7
- package/src/providers/integrations/trae/trae.ts +51 -8
- package/src/providers/integrations/xai/xai-oauth.ts +5 -5
- package/src/providers/integrations/zcode-start-plan/gifts.ts +265 -0
- package/src/providers/integrations/zcode-start-plan/oauth.ts +154 -0
- package/src/providers/integrations/zcode-start-plan/provider.ts +131 -0
- package/src/providers/integrations/zcode-start-plan/quota.ts +152 -0
- package/src/providers/integrations/zcode-start-plan/zcode_prompt.json +10 -0
- package/src/providers/operations/account-error-classification.ts +198 -133
- package/src/providers/operations/account-health-service.ts +31 -39
- package/src/providers/operations/account-reset-service.ts +58 -1
- package/src/providers/operations/byok-wire-profile.ts +19 -15
- package/src/providers/operations/cli-platform.ts +31 -5
- package/src/providers/operations/client-version-resolver.ts +4 -6
- package/src/providers/operations/client-versions.ts +10 -7
- package/src/providers/operations/provider-catalog-seeder.ts +2 -0
- package/src/providers/operations/provider-catalog-service.ts +18 -2
- package/src/providers/operations/provider-credential-service.ts +19 -2
- package/src/providers/provider-contracts.ts +18 -7
- package/src/providers/provider-metadata.ts +17 -2
- package/src/providers/provider-model-definition.ts +22 -1
- package/src/providers/provider-probe.ts +4 -2
- package/src/providers/provider-usage.ts +50 -19
- package/src/providers/quota/cache.ts +29 -0
- package/src/providers/quota/quota-contracts.ts +4 -0
- package/src/providers/quota/refresh.ts +27 -4
- package/src/providers/quota/snapshot.ts +94 -0
- package/src/providers/search/search-provider.ts +1 -3
- package/src/providers/search/search-providers.ts +2 -1
- package/src/security/abuse.ts +4 -4
- package/src/security/admission/service.ts +13 -6
- package/src/security/admission/ttl.ts +61 -61
- package/src/security/api-key-auth.ts +17 -3
- package/src/security/client-router-fingerprint.ts +4 -4
- package/src/security/credential-codec.ts +3 -3
- package/src/security/csrf.ts +2 -2
- package/src/security/ip-boundary.ts +16 -189
- package/src/security/model-access-rule.ts +1 -1
- package/src/security/outbound-headers.ts +18 -0
- package/src/shared/invoke-guard.ts +47 -0
- package/src/transport/contracts.ts +13 -4
- package/src/transport/dispatch/attempt-finalize.ts +70 -22
- package/src/transport/dispatch/attempt-loop.ts +22 -0
- package/src/transport/dispatch/capture-policy.ts +34 -37
- package/src/transport/dispatch/empty-stream.ts +23 -20
- package/src/transport/dispatch/generation.ts +25 -45
- package/src/transport/dispatch/native-shared.ts +127 -0
- package/src/transport/dispatch/proxy-request.ts +3 -1
- package/src/transport/dispatch/responses-compact.ts +25 -46
- package/src/transport/dispatch/streaming/encoder.ts +18 -72
- package/src/transport/dispatch/streaming/finalize.ts +2 -2
- package/src/transport/dispatch/streaming/stream.ts +7 -3
- package/src/transport/dispatch/streaming/thinking-loop.ts +1 -1
- package/src/transport/dispatch/systemone.ts +20 -48
- package/src/transport/dispatch/token-count.ts +41 -38
- package/src/transport/dispatch/websearch.ts +19 -45
- package/src/transport/drain-endpoint.ts +1 -1
- package/src/transport/failure-policy.ts +21 -1
- package/src/transport/guards/auth-guard.ts +0 -6
- package/src/transport/guards/body-policy.ts +3 -3
- package/src/transport/guards/error-lifecycle.ts +4 -0
- package/src/transport/guards/headroom-guard.ts +1 -1
- package/src/transport/guards/pipeline.ts +1 -1
- package/src/transport/guards/request-context.ts +12 -17
- package/src/transport/request/preparer.ts +286 -7
- package/src/transport/request/rtk/compress-request.ts +3 -3
- package/src/transport/request/rtk/rtk-constants.ts +2 -2
- package/src/transport/request/rtk/rtk-filters.ts +8 -8
- package/src/transport/request/state.ts +7 -13
- package/src/transport/request/system-prompt-injection.ts +74 -11
- package/src/transport/request/tenant-preferences.ts +22 -0
- package/src/transport/routing/candidate-index.ts +2 -2
- package/src/transport/routing/round-robin.ts +1 -1
- package/src/transport/routing/route-catalog.ts +17 -18
- package/src/transport/routing/route-model.ts +126 -33
- package/src/transport/routing/router.ts +159 -37
- package/src/transport/shutdown-notice.ts +2 -2
- package/src/transport/surface/adapters.ts +3 -3
- package/src/transport/surface/chat/encode.ts +7 -7
- package/src/transport/surface/chat/parse.ts +7 -7
- package/src/transport/surface/completion.ts +2 -2
- package/src/transport/surface/content-parts.ts +1 -1
- package/src/transport/surface/dialects.ts +2 -2
- package/src/transport/surface/messages/parse.ts +3 -3
- package/src/transport/surface/reasoning-summary.ts +9 -14
- package/src/transport/surface/responses/adapter.ts +1 -1
- package/src/transport/surface/responses/errors.ts +0 -9
- package/src/transport/vision-handoff-cache.ts +58 -0
- package/src/transport/vision-handoff.ts +431 -0
- package/src/workers/account-identity-sweep.ts +2 -2
- package/src/workers/buddy-growth-tasks.ts +392 -346
- package/src/workers/daily-checkin.ts +102 -49
- package/src/workers/maintenance-lease.ts +2 -35
- package/src/workers/oauth-refresh-sweep.ts +8 -1
- package/src/workers/quota-refresh-sweep.ts +120 -82
- package/src/workers/scheduler.ts +67 -4
- package/src/workers/sweep-runner.ts +24 -5
- package/src/workers/zcode-gift-claim.ts +108 -0
- package/dist/dashboard/assets/AccountCooldown-Bjo92Qat.js +0 -1
- package/dist/dashboard/assets/AccountCooldown-Bjo92Qat.js.gz +0 -0
- package/dist/dashboard/assets/BannedPage-ChG5svFb.js +0 -1
- package/dist/dashboard/assets/BannedPage-ChG5svFb.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-BIcWQun7.js +0 -1
- package/dist/dashboard/assets/CliToolDetailPage-BIcWQun7.js.gz +0 -0
- package/dist/dashboard/assets/CliToolsPage-Bf2VelDe.js +0 -1
- package/dist/dashboard/assets/CliToolsPage-Bf2VelDe.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-DCgm5g6g.js +0 -1
- package/dist/dashboard/assets/CombosPage-DCgm5g6g.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-a8mD0QF1.js +0 -1
- package/dist/dashboard/assets/ConsoleLogPage-a8mD0QF1.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-ggeMSjte.js +0 -1
- package/dist/dashboard/assets/CustomizationPage-ggeMSjte.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-DzSWzE_j.js +0 -1
- package/dist/dashboard/assets/HealthEventsModal-DzSWzE_j.js.gz +0 -0
- package/dist/dashboard/assets/LoginPage-U6AKaJcF.js +0 -1
- package/dist/dashboard/assets/LoginPage-U6AKaJcF.js.gz +0 -0
- package/dist/dashboard/assets/ModelPicker-BMS2t8BA.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-D2D4IiHl.js +0 -1
- package/dist/dashboard/assets/OverviewPage-D2D4IiHl.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-CrNf7j_X.js +0 -5
- package/dist/dashboard/assets/ProviderDetailPage-CrNf7j_X.js.gz +0 -0
- package/dist/dashboard/assets/ProviderIcon-FB6yS4v1.js +0 -1
- package/dist/dashboard/assets/ProviderIcon-FB6yS4v1.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-BSDbquc1.js +0 -1
- package/dist/dashboard/assets/ProvidersPage-BSDbquc1.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-lsrx7NYg.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-L0skzML8.js +0 -1
- package/dist/dashboard/assets/QuotaPage-L0skzML8.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-CmAbjRHW.js +0 -1
- package/dist/dashboard/assets/SettingsPage-CmAbjRHW.js.gz +0 -0
- package/dist/dashboard/assets/SetupPage-f0DE1h0-.js +0 -1
- package/dist/dashboard/assets/SetupPage-f0DE1h0-.js.gz +0 -0
- package/dist/dashboard/assets/SortableList-ClzpwyET.js +0 -1
- package/dist/dashboard/assets/SortableList-ClzpwyET.js.gz +0 -0
- package/dist/dashboard/assets/StudioPage-Cq5tW7hW.js.gz +0 -0
- package/dist/dashboard/assets/ToolIcon-K1ckcDKg.js +0 -1
- package/dist/dashboard/assets/ToolIcon-K1ckcDKg.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-Dfc9rWQn.js +0 -56
- package/dist/dashboard/assets/UsagePage-Dfc9rWQn.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-C38iXoO4.js +0 -1
- package/dist/dashboard/assets/api-keys-C38iXoO4.js.gz +0 -0
- package/dist/dashboard/assets/app-BayTTy01.js +0 -9
- package/dist/dashboard/assets/app-BayTTy01.js.gz +0 -0
- package/dist/dashboard/assets/app-a159k0ub.css +0 -2
- package/dist/dashboard/assets/app-a159k0ub.css.gz +0 -0
- package/dist/dashboard/assets/assert-DbRA8EG2.js +0 -15
- package/dist/dashboard/assets/assert-DbRA8EG2.js.gz +0 -0
- package/dist/dashboard/assets/badge-D9CS6dZk.js.gz +0 -0
- package/dist/dashboard/assets/banner-DympBNq4.js.gz +0 -0
- package/dist/dashboard/assets/card-ibRWU_uT.js.gz +0 -0
- package/dist/dashboard/assets/cli-tools-63qBGaRw.js +0 -1
- package/dist/dashboard/assets/cli-tools-63qBGaRw.js.gz +0 -0
- package/dist/dashboard/assets/clipboard-button-DJm-9pz7.js.gz +0 -0
- package/dist/dashboard/assets/dialog-8fiE6tec.js +0 -1
- package/dist/dashboard/assets/dialog-8fiE6tec.js.gz +0 -0
- package/dist/dashboard/assets/drawer-CqGYeOrd.js.gz +0 -0
- package/dist/dashboard/assets/entry-BSAsYsmV.js +0 -1
- package/dist/dashboard/assets/entry-BSAsYsmV.js.gz +0 -0
- package/dist/dashboard/assets/entry-CiIQULZD.js +0 -4
- package/dist/dashboard/assets/entry-CiIQULZD.js.gz +0 -0
- package/dist/dashboard/assets/entry-Daz0lJ-W.js +0 -1
- package/dist/dashboard/assets/entry-Daz0lJ-W.js.gz +0 -0
- package/dist/dashboard/assets/formatters-D-MTJxMr.js.gz +0 -0
- package/dist/dashboard/assets/github-badge-CPK47xQx.js +0 -1
- package/dist/dashboard/assets/github-badge-CPK47xQx.js.gz +0 -0
- package/dist/dashboard/assets/index-DBNPOfhV.js.gz +0 -0
- package/dist/dashboard/assets/inline-BT0zntEr.js.gz +0 -0
- package/dist/dashboard/assets/input-Da2ZViWg.js.gz +0 -0
- package/dist/dashboard/assets/layout-BXNeLw6M.js.gz +0 -0
- package/dist/dashboard/assets/list-row-BycOrrIV.js.gz +0 -0
- package/dist/dashboard/assets/live-DCQNZpF7.js +0 -1
- package/dist/dashboard/assets/live-DCQNZpF7.js.gz +0 -0
- package/dist/dashboard/assets/mutation-Rr0TIvcj.js.gz +0 -0
- package/dist/dashboard/assets/network-9Yj2ZyLo.js +0 -1
- package/dist/dashboard/assets/network-9Yj2ZyLo.js.gz +0 -0
- package/dist/dashboard/assets/numbers-CEagj6ZZ.js +0 -2
- package/dist/dashboard/assets/numbers-CEagj6ZZ.js.gz +0 -0
- package/dist/dashboard/assets/providers/zai-coding-plan.svg +0 -6
- package/dist/dashboard/assets/providers/zai-coding-plan.svg.gz +0 -0
- package/dist/dashboard/assets/quota-Caw7wqci.js +0 -1
- package/dist/dashboard/assets/quota-Caw7wqci.js.gz +0 -0
- package/dist/dashboard/assets/routing-CO9zCUus.js +0 -1
- package/dist/dashboard/assets/routing-CO9zCUus.js.gz +0 -0
- package/dist/dashboard/assets/select-BIYIBkMU.js.gz +0 -0
- package/dist/dashboard/assets/settings-jS3-PNSA.js +0 -1
- package/dist/dashboard/assets/settings-jS3-PNSA.js.gz +0 -0
- package/dist/dashboard/assets/stack-DfgnFDWY.js.gz +0 -0
- package/dist/dashboard/assets/state-CkfdYWel.js.gz +0 -0
- package/dist/dashboard/assets/stream-retry-5QnVuSSo.js +0 -1
- package/dist/dashboard/assets/stream-retry-5QnVuSSo.js.gz +0 -0
- package/dist/dashboard/assets/switch-SYm237HG.js.gz +0 -0
- package/dist/dashboard/assets/useMutation-BQqCRNIi.js.gz +0 -0
- package/src/console/cli-tools/injectors/cline.ts +0 -110
- package/src/console/cli-tools/injectors/copilot.ts +0 -108
- package/src/console/cli-tools/injectors/cowork.ts +0 -64
- package/src/console/cli-tools/injectors/deepseek-tui.ts +0 -85
- package/src/console/cli-tools/injectors/droid.ts +0 -96
- package/src/console/cli-tools/injectors/grok-build.ts +0 -70
- package/src/console/cli-tools/injectors/hermes.ts +0 -101
- package/src/console/cli-tools/injectors/jcode.ts +0 -115
- package/src/console/cli-tools/injectors/openclaw.ts +0 -161
- package/src/network/response-headers.ts +0 -39
- package/src/protocol/response/dsml.ts +0 -38
- package/src/providers/integrations/xiaomi-mimo/think-stream.ts +0 -229
- /package/dist/dashboard/assets/brand/{og_bansos.webp → og_bansos_initial.webp} +0 -0
- /package/dist/dashboard/assets/brand/{og_image.webp → og_image_initial.webp} +0 -0
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
type ProviderRoutingSetting,
|
|
13
13
|
type Reservation,
|
|
14
14
|
type RouteCandidate,
|
|
15
|
+
type RoutedCandidate,
|
|
15
16
|
type RoutePlan,
|
|
16
17
|
type EligibilityDecision,
|
|
17
18
|
type RouteContinuationAffinity,
|
|
@@ -34,22 +35,100 @@ import {
|
|
|
34
35
|
} from "./candidate-index";
|
|
35
36
|
import { totalRemainingCredit, totalRemainingPercent, type ProviderQuotaWindow } from "../../providers/quota/quota-contracts";
|
|
36
37
|
import { getCachedQuotaEntry, quotaLens } from "../../providers/quota/cache";
|
|
38
|
+
import { readQuotaSnapshot } from "../../providers/quota/snapshot";
|
|
39
|
+
import type { CartethyiaDatabase } from "../../persistence/db-handle";
|
|
37
40
|
import type { RedisClient } from "../../persistence/redis";
|
|
38
41
|
import { sharedClaudeSlowLanes } from "../../providers/integrations/claude/claude-slow-lane";
|
|
39
42
|
|
|
43
|
+
/**
|
|
44
|
+
* Filters quota windows to those relevant to the candidate model.
|
|
45
|
+
* Providers like Antigravity report separate quotas for distinct model families
|
|
46
|
+
* (Gemini vs Claude/GPT), and Grok reports per-product buckets.
|
|
47
|
+
* Evaluating a Claude exhaustion against a Gemini request would incorrectly
|
|
48
|
+
* mark the account exhausted for Gemini when Gemini capacity is still available.
|
|
49
|
+
*/
|
|
50
|
+
export function filterCandidateQuotaWindows(
|
|
51
|
+
windows: readonly ProviderQuotaWindow[],
|
|
52
|
+
candidate: { readonly provider_id?: string; readonly model_id?: string },
|
|
53
|
+
): readonly ProviderQuotaWindow[] {
|
|
54
|
+
if (windows.length === 0) return windows;
|
|
55
|
+
const providerId = candidate.provider_id?.toLowerCase() ?? "";
|
|
56
|
+
const modelId = candidate.model_id?.toLowerCase() ?? "";
|
|
57
|
+
windows = windows.filter((window) => window.modelIds === undefined || window.modelIds.includes(modelId));
|
|
58
|
+
|
|
59
|
+
if (providerId === "antigravity") {
|
|
60
|
+
const isGemini = /gemini|tab_flash/i.test(modelId);
|
|
61
|
+
const isClaude = /claude|opus|sonnet/i.test(modelId);
|
|
62
|
+
if (isGemini) {
|
|
63
|
+
return windows.filter((w) => {
|
|
64
|
+
const text = `${w.kind} ${w.label}`.toLowerCase();
|
|
65
|
+
if (text.includes("gemini") || text.includes("google")) return true;
|
|
66
|
+
return !text.includes("claude") && !text.includes("gpt");
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
if (isClaude) {
|
|
70
|
+
return windows.filter((w) => {
|
|
71
|
+
const text = `${w.kind} ${w.label}`.toLowerCase();
|
|
72
|
+
if (text.includes("claude") || text.includes("gpt")) return true;
|
|
73
|
+
return !text.includes("gemini") && !text.includes("google");
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (providerId === "grok") {
|
|
79
|
+
return windows.filter((w) => {
|
|
80
|
+
if (w.kind.startsWith("product:")) {
|
|
81
|
+
const product = w.kind.split(":")[1]?.toLowerCase();
|
|
82
|
+
return product ? modelId.includes(product) : true;
|
|
83
|
+
}
|
|
84
|
+
return true;
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return windows;
|
|
89
|
+
}
|
|
90
|
+
|
|
40
91
|
/**
|
|
41
92
|
* True when the account's remaining allowance is at or below its provider's
|
|
42
93
|
* floor. Credit mode sums the absolute remaining credit; percent mode uses the
|
|
43
94
|
* aggregate credit percentage and the lowest independent percentage window.
|
|
44
95
|
* No reading means unknown, which is not below the floor.
|
|
45
96
|
*/
|
|
46
|
-
function belowUsageFloor(state: {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
if (
|
|
97
|
+
function belowUsageFloor(state: RoutedCandidate): boolean {
|
|
98
|
+
const rawWindows = state.quota_windows;
|
|
99
|
+
// A successful Start Plan balance (`quota_entitlements_known`) authoritatively
|
|
100
|
+
// lists its model grants: when the provider is zcode-start and no window
|
|
101
|
+
// matches the requested model, the account is below the floor even with an
|
|
102
|
+
// empty/absent window list — there is no grant to fund the model.
|
|
103
|
+
if (state.provider_id === "zcode-start" && state.quota_entitlements_known === true) {
|
|
104
|
+
if (rawWindows === undefined || rawWindows.length === 0) return true;
|
|
105
|
+
const knownGrants = rawWindows.some((window) => window.modelIds !== undefined);
|
|
106
|
+
if (!knownGrants) return true;
|
|
107
|
+
}
|
|
108
|
+
if (rawWindows === undefined || rawWindows.length === 0) return false;
|
|
109
|
+
const relevant = filterCandidateQuotaWindows(rawWindows, state);
|
|
110
|
+
// Model-scoped grants (Start Plan) are checked separately: a grant that has
|
|
111
|
+
// not started, has expired, or is exhausted cannot fund the requested model,
|
|
112
|
+
// so it must not count as credit.
|
|
113
|
+
const now = Date.now();
|
|
114
|
+
const grants = relevant.filter((window) => window.modelIds !== undefined);
|
|
115
|
+
if (state.provider_id === "zcode-start" && grants.length === 0 && rawWindows.some((window) => window.modelIds !== undefined)) {
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
if (
|
|
119
|
+
grants.length > 0 &&
|
|
120
|
+
!grants.some((window) => {
|
|
121
|
+
if (window.startsAt && Date.parse(window.startsAt) > now) return false;
|
|
122
|
+
if (window.resetsAt && Date.parse(window.resetsAt) <= now) return false;
|
|
123
|
+
if (typeof window.remaining === "number" && window.remaining <= 0) return false;
|
|
124
|
+
if (typeof window.remainingPercent === "number" && window.remainingPercent <= 0) return false;
|
|
125
|
+
const remaining = state.usage_floor_unit === "credit" ? window.remaining : window.remainingPercent;
|
|
126
|
+
return state.usage_floor === undefined || remaining == null || remaining > state.usage_floor;
|
|
127
|
+
})
|
|
128
|
+
) {
|
|
129
|
+
return true;
|
|
130
|
+
}
|
|
131
|
+
const windows = relevant.filter((window) => window.modelIds === undefined);
|
|
53
132
|
const credit = totalRemainingCredit(windows);
|
|
54
133
|
const percent = totalRemainingPercent(windows);
|
|
55
134
|
const exhaustedIndependentWindow = windows.some(
|
|
@@ -63,10 +142,32 @@ function belowUsageFloor(state: {
|
|
|
63
142
|
if (credit === null && percent !== null && percent <= 0) return true;
|
|
64
143
|
const floor = state.usage_floor;
|
|
65
144
|
if (floor === undefined) return false;
|
|
145
|
+
// Free-tier models are not credit-funded, so the operator's configured floor
|
|
146
|
+
// threshold does not apply to them — a free model may use any remaining
|
|
147
|
+
// balance. This mirrors `creditPriority` (free tier skips credit preference).
|
|
148
|
+
// It deliberately comes AFTER the exhausted-window checks above: a free-tier
|
|
149
|
+
// account whose own quota window is spent (0 left) is still excluded, because
|
|
150
|
+
// that is the provider's own limit, not the credit floor.
|
|
151
|
+
if (state.free_tier === true) return false;
|
|
66
152
|
const remaining = state.usage_floor_unit === "credit" ? credit : percent;
|
|
67
153
|
return remaining !== null && remaining <= floor;
|
|
68
154
|
}
|
|
69
155
|
|
|
156
|
+
/**
|
|
157
|
+
* Routing priority for an account's remaining allowance when two candidates sit
|
|
158
|
+
* in the same rank group: positive remaining credit first, then unknown, then
|
|
159
|
+
* known-zero last. Free-tier models skip the preference entirely — a free model
|
|
160
|
+
* routes to any account, as the provider's own directory stated it is free.
|
|
161
|
+
*/
|
|
162
|
+
function creditPriority(candidate: RoutedCandidate): number {
|
|
163
|
+
if (candidate.free_tier === true) return 0;
|
|
164
|
+
const windows = candidate.quota_windows;
|
|
165
|
+
if (windows === undefined || windows.length === 0) return 0;
|
|
166
|
+
const credit = totalRemainingCredit(windows);
|
|
167
|
+
if (credit === null) return 0;
|
|
168
|
+
return credit > 0 ? 1 : -1;
|
|
169
|
+
}
|
|
170
|
+
|
|
70
171
|
/**
|
|
71
172
|
* Normalizes a requested model name before alias lookup. Coding-agent CLIs
|
|
72
173
|
* version their model ids with variant suffixes (`claude-opus-5[1m]`,
|
|
@@ -233,10 +334,17 @@ function spreadDeadline(cooldownUntil: number | undefined): { cooldown_until?: n
|
|
|
233
334
|
function earliestWindowResetMs(
|
|
234
335
|
windows: readonly ProviderQuotaWindow[] | undefined,
|
|
235
336
|
nowMs: number,
|
|
337
|
+
candidate: RouteCandidate,
|
|
236
338
|
): number | undefined {
|
|
237
339
|
if (windows === undefined) return undefined;
|
|
340
|
+
windows = filterCandidateQuotaWindows(windows, candidate);
|
|
238
341
|
let earliest: number | undefined;
|
|
239
342
|
for (const window of windows) {
|
|
343
|
+
if (window.startsAt && Date.parse(window.startsAt) > nowMs) {
|
|
344
|
+
const at = Date.parse(window.startsAt);
|
|
345
|
+
if (earliest === undefined || at < earliest) earliest = at;
|
|
346
|
+
continue;
|
|
347
|
+
}
|
|
240
348
|
if (typeof window.resetsAt !== "string" || window.resetsAt.length === 0) continue;
|
|
241
349
|
const at = Date.parse(window.resetsAt);
|
|
242
350
|
if (!Number.isFinite(at) || at <= nowMs) continue;
|
|
@@ -249,7 +357,7 @@ function earliestWindowResetMs(
|
|
|
249
357
|
* The earliest instant any cooling candidate recovers, in epoch ms.
|
|
250
358
|
*
|
|
251
359
|
* `undefined` when no decision carries a deadline — which is the honest answer
|
|
252
|
-
* for a set whose exclusions have no reset (`disabled
|
|
360
|
+
* for a set whose exclusions have no reset (`disabled`), and keeps
|
|
253
361
|
* `accountsRateLimitedError` from reporting a number nobody measured. The
|
|
254
362
|
* minimum, not the maximum: the client can be served as soon as the *first*
|
|
255
363
|
* account comes back, so quoting the slowest would hold it off longer than it
|
|
@@ -268,36 +376,22 @@ function earliestResetMs(
|
|
|
268
376
|
}
|
|
269
377
|
|
|
270
378
|
export class EligibilityEvaluator {
|
|
271
|
-
evaluate(candidate:
|
|
272
|
-
|
|
273
|
-
readonly health_status?: string;
|
|
274
|
-
readonly cooldown_kind?: "hard" | "soft";
|
|
275
|
-
readonly cooldown_until?: number;
|
|
276
|
-
readonly account_locked?: boolean;
|
|
277
|
-
readonly locked?: boolean;
|
|
278
|
-
readonly usage_floor?: number;
|
|
279
|
-
readonly usage_floor_unit?: "credit" | "percent";
|
|
280
|
-
readonly quota_windows?: readonly ProviderQuotaWindow[] | undefined;
|
|
281
|
-
/** Slow-lane stamp applied at plan time; see `applySlowLane`. */
|
|
282
|
-
readonly lane?: "slow";
|
|
283
|
-
};
|
|
284
|
-
if (state.account_locked || state.locked)
|
|
285
|
-
return { eligible: false, reason: "locked", candidate };
|
|
286
|
-
if (state.health_status === "disabled")
|
|
379
|
+
evaluate(candidate: RoutedCandidate): EligibilityDecision {
|
|
380
|
+
if (candidate.health_status === "disabled")
|
|
287
381
|
return { eligible: false, reason: "disabled", candidate };
|
|
288
|
-
if (
|
|
289
|
-
return { eligible: false, reason: "model_cooldown", candidate, ...spreadDeadline(
|
|
290
|
-
if (
|
|
291
|
-
return { eligible: false, reason: "cooldown_hard", candidate, ...spreadDeadline(
|
|
382
|
+
if (candidate.health_status === "model_cooldown")
|
|
383
|
+
return { eligible: false, reason: "model_cooldown", candidate, ...spreadDeadline(candidate.cooldown_until) };
|
|
384
|
+
if (candidate.health_status === "cooldown" && candidate.cooldown_kind === "hard")
|
|
385
|
+
return { eligible: false, reason: "cooldown_hard", candidate, ...spreadDeadline(candidate.cooldown_until) };
|
|
292
386
|
// The floor is checked here, not after selection: an account below its
|
|
293
387
|
// floor must not win the route. An account with no reading is left
|
|
294
388
|
// eligible — a missing quota fetch is not proof the allowance is gone.
|
|
295
|
-
if (belowUsageFloor(
|
|
389
|
+
if (belowUsageFloor(candidate))
|
|
296
390
|
return {
|
|
297
391
|
eligible: false,
|
|
298
392
|
reason: "quota_exhausted",
|
|
299
393
|
candidate,
|
|
300
|
-
...spreadDeadline(earliestWindowResetMs(
|
|
394
|
+
...spreadDeadline(earliestWindowResetMs(candidate.quota_windows, Date.now(), candidate)),
|
|
301
395
|
};
|
|
302
396
|
// A slow lane is orthogonal to cooldown, never a member of it: the account
|
|
303
397
|
// still serves on spare capacity when the request carries the slow usage
|
|
@@ -308,10 +402,10 @@ export class EligibilityEvaluator {
|
|
|
308
402
|
// exclusion, not that it overrides one. An account with no usable allowance
|
|
309
403
|
// is not rescued by the lane — the lane is a latency budget against spare
|
|
310
404
|
// capacity, and an account below its floor has none.
|
|
311
|
-
if (
|
|
405
|
+
if (candidate.lane === "slow")
|
|
312
406
|
return { eligible: true, reason: "slow", candidate };
|
|
313
|
-
if (
|
|
314
|
-
return { eligible: true, reason: "cooldown", candidate, ...spreadDeadline(
|
|
407
|
+
if (candidate.health_status === "cooldown")
|
|
408
|
+
return { eligible: true, reason: "cooldown", candidate, ...spreadDeadline(candidate.cooldown_until) };
|
|
315
409
|
return { eligible: true, reason: "healthy", candidate };
|
|
316
410
|
}
|
|
317
411
|
|
|
@@ -332,6 +426,7 @@ export class RoutingEngine {
|
|
|
332
426
|
constructor(
|
|
333
427
|
private readonly admission: AdmissionController = new InMemoryAdmissionController(),
|
|
334
428
|
private readonly redis?: RedisClient,
|
|
429
|
+
private readonly db?: CartethyiaDatabase,
|
|
335
430
|
) {}
|
|
336
431
|
|
|
337
432
|
private getRoundRobin(key: string): RoundRobinState {
|
|
@@ -467,13 +562,32 @@ export class RoutingEngine {
|
|
|
467
562
|
accountId,
|
|
468
563
|
this.redis,
|
|
469
564
|
).catch(() => undefined);
|
|
470
|
-
|
|
565
|
+
// The live cache is authoritative when warm; when it is cold (e.g. right
|
|
566
|
+
// after a restart, before the sweep re-reads accounts), fall back to the
|
|
567
|
+
// persisted quota snapshot — the durable source of truth — so routing
|
|
568
|
+
// still prefers accounts that actually have remaining credit. The snapshot
|
|
569
|
+
// is never fresher than the cache, but a stale balance beats an unknown
|
|
570
|
+
// one for ordering, and the sweep refreshes it on schedule.
|
|
571
|
+
let quota = cached?.quota;
|
|
572
|
+
if (quota === undefined && this.db !== undefined) {
|
|
573
|
+
const stored = await readQuotaSnapshot(this.db, accountId).catch(() => null);
|
|
574
|
+
quota = stored?.quota;
|
|
575
|
+
}
|
|
576
|
+
const rawWindows = quota?.windows;
|
|
577
|
+
// The cache is authoritative when it has a reading; when it does not,
|
|
578
|
+
// keep whatever the candidate already carried (the credit ordering reads
|
|
579
|
+
// these windows, and a miss must not erase a stamped value).
|
|
580
|
+
const quotaWindows =
|
|
581
|
+
rawWindows === undefined
|
|
582
|
+
? undefined
|
|
583
|
+
: candidate.provider_id === "zcode-start" ? rawWindows : filterCandidateQuotaWindows(rawWindows, candidate);
|
|
471
584
|
return {
|
|
472
585
|
...candidate,
|
|
473
586
|
...(setting?.usageFloor == null
|
|
474
587
|
? {}
|
|
475
588
|
: { usage_floor: setting.usageFloor, usage_floor_unit: setting.usageFloorUnit }),
|
|
476
|
-
quota_windows: quotaWindows,
|
|
589
|
+
...(quotaWindows === undefined ? {} : { quota_windows: quotaWindows }),
|
|
590
|
+
...(quota?.source === "zcode-start" && quota.error === null ? { quota_entitlements_known: true } : {}),
|
|
477
591
|
};
|
|
478
592
|
}
|
|
479
593
|
|
|
@@ -641,6 +755,11 @@ export class RoutingEngine {
|
|
|
641
755
|
const pinnedDecisions = pinned.map((candidate) => this.eligibility.evaluate(candidate));
|
|
642
756
|
const blocked = pinnedDecisions.find((decision) => !decision.eligible);
|
|
643
757
|
if (blocked !== undefined) {
|
|
758
|
+
// A rebaseable continuation (complete conversation replay) is not owed
|
|
759
|
+
// to this account: the recorded exchange may travel as history to a
|
|
760
|
+
// fresh Run on an eligible account. Cooling/blocked pinned account must
|
|
761
|
+
// not turn that into a hard refusal — fall through to normal routing.
|
|
762
|
+
if (affinity.rebaseable) return null;
|
|
644
763
|
const reason = blocked.reason;
|
|
645
764
|
if (
|
|
646
765
|
reason === "cooldown_hard" ||
|
|
@@ -810,11 +929,14 @@ export class RoutingEngine {
|
|
|
810
929
|
);
|
|
811
930
|
}
|
|
812
931
|
|
|
813
|
-
// Single sort by rank (fresh → slow → cooling → saturated), stable.
|
|
932
|
+
// Single sort by rank (fresh → slow → cooling → saturated), stable. Within
|
|
933
|
+
// the same rank, accounts with remaining credit come first unless the model
|
|
934
|
+
// is free tier, which routes to any account.
|
|
814
935
|
eligible = [...eligible].sort((left, right) => {
|
|
815
936
|
const leftRank = decisions.get(admissionKey(left))?.rank ?? ROUTING_RANK.FRESH;
|
|
816
937
|
const rightRank = decisions.get(admissionKey(right))?.rank ?? ROUTING_RANK.FRESH;
|
|
817
|
-
return leftRank - rightRank;
|
|
938
|
+
if (leftRank !== rightRank) return leftRank - rightRank;
|
|
939
|
+
return creditPriority(right) - creditPriority(left);
|
|
818
940
|
});
|
|
819
941
|
|
|
820
942
|
// Provider routing rotates accounts within the same rank group only; it can
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
*/
|
|
16
16
|
import { GatewayError, type GatewayErrorCode } from "../protocol/gateway-error";
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
interface ShutdownNotice {
|
|
19
19
|
readonly code: Extract<GatewayErrorCode, "shutting_down">;
|
|
20
20
|
readonly message: string;
|
|
21
21
|
readonly reason?: string;
|
|
@@ -27,7 +27,7 @@ export interface ShutdownNotice {
|
|
|
27
27
|
* with it before swapping the image so its callers are told the process is
|
|
28
28
|
* coming straight back.
|
|
29
29
|
*/
|
|
30
|
-
|
|
30
|
+
const UPDATE_SHUTDOWN_REASON = "update";
|
|
31
31
|
|
|
32
32
|
export function shutdownNotice(reason: string | undefined): ShutdownNotice {
|
|
33
33
|
if (reason === UPDATE_SHUTDOWN_REASON) {
|
|
@@ -26,7 +26,7 @@ export function eventText(event: CanonicalEvent): string | undefined {
|
|
|
26
26
|
// The endpoint path is authoritative: user-agent sniffing and prompt-text
|
|
27
27
|
// marker scanning never override it, so routing cannot depend on client
|
|
28
28
|
// identity strings or prompt content.
|
|
29
|
-
|
|
29
|
+
type WinningSource =
|
|
30
30
|
"explicit_marker" | "body_shape" | "endpoint_path";
|
|
31
31
|
|
|
32
32
|
export interface SurfaceInput {
|
|
@@ -35,7 +35,7 @@ export interface SurfaceInput {
|
|
|
35
35
|
path: string;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
interface SurfaceDetection {
|
|
39
39
|
surface: SourceSurface;
|
|
40
40
|
winning_source: WinningSource;
|
|
41
41
|
/**
|
|
@@ -64,7 +64,7 @@ export interface SurfaceAdapter {
|
|
|
64
64
|
* it explicitly. Both detection lookups read this table, so adding a surface
|
|
65
65
|
* is one row here plus its adapter.
|
|
66
66
|
*/
|
|
67
|
-
|
|
67
|
+
interface SurfaceDescriptor {
|
|
68
68
|
readonly surface: SourceSurface;
|
|
69
69
|
readonly pathPrefix: string;
|
|
70
70
|
}
|
|
@@ -37,8 +37,8 @@ import type { ChatEncodeOptions, ChunkBase, JsonObject } from "./parse";
|
|
|
37
37
|
*/
|
|
38
38
|
export function repairToolArguments(raw: string): string {
|
|
39
39
|
if (raw.length === 0) return "{}";
|
|
40
|
-
const repaired = repairToolArgumentText(raw);
|
|
41
40
|
try {
|
|
41
|
+
const repaired = repairToolArgumentText(raw);
|
|
42
42
|
const parsed: unknown = JSON.parse(repaired);
|
|
43
43
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) return raw;
|
|
44
44
|
return repaired;
|
|
@@ -52,7 +52,7 @@ function repairToolArgumentText(json: string): string {
|
|
|
52
52
|
return jsonrepair(json);
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
function bytesToString(value: string | Uint8Array): string {
|
|
56
56
|
return typeof value === "string" ? value : new TextDecoder().decode(value);
|
|
57
57
|
}
|
|
58
58
|
|
|
@@ -84,7 +84,7 @@ export function getModel(options: ChatEncodeOptions, events: CanonicalEvent[]):
|
|
|
84
84
|
* placeholder — passing the provider's own bytes through is the whole point of
|
|
85
85
|
* the extension — but never an explicit configuration.
|
|
86
86
|
*/
|
|
87
|
-
|
|
87
|
+
function resolveObfuscation(options: ChatEncodeOptions): {
|
|
88
88
|
value: string | undefined;
|
|
89
89
|
pinned: boolean;
|
|
90
90
|
} {
|
|
@@ -96,7 +96,7 @@ export function resolveObfuscation(options: ChatEncodeOptions): {
|
|
|
96
96
|
return { value: undefined, pinned: false };
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
-
|
|
99
|
+
function eventObfuscation(event: CanonicalEvent): string | undefined {
|
|
100
100
|
if (event.type !== "content_delta" || event.content.kind !== "extension") return undefined;
|
|
101
101
|
if (!event.content.name.includes("obfuscat")) return undefined;
|
|
102
102
|
const payload = event.content.payload;
|
|
@@ -106,13 +106,13 @@ export function eventObfuscation(event: CanonicalEvent): string | undefined {
|
|
|
106
106
|
}
|
|
107
107
|
|
|
108
108
|
/** Audio output payload carried as an `audio` extension content part. */
|
|
109
|
-
|
|
109
|
+
function eventAudio(event: CanonicalEvent): Record<string, unknown> | undefined {
|
|
110
110
|
if (event.type !== "content_delta" || event.content.kind !== "extension") return undefined;
|
|
111
111
|
if (event.content.name !== "audio") return undefined;
|
|
112
112
|
return isRecord(event.content.payload) ? event.content.payload : undefined;
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
-
|
|
115
|
+
function eventReasoningText(event: CanonicalEvent): string | undefined {
|
|
116
116
|
if (event.type !== "content_delta" || event.content.kind !== "reasoning") return undefined;
|
|
117
117
|
return reasoningTextOf(event.content);
|
|
118
118
|
}
|
|
@@ -144,7 +144,7 @@ function mergedReasoningText(events: readonly CanonicalEvent[]): string {
|
|
|
144
144
|
export function finishReason(stopReason: CanonicalStopReason | undefined): string {
|
|
145
145
|
return chatFinishReason(stopReason);
|
|
146
146
|
}
|
|
147
|
-
|
|
147
|
+
function eventToolCall(
|
|
148
148
|
event: CanonicalEvent,
|
|
149
149
|
): { state: ToolCallState; hasArguments: boolean } | undefined {
|
|
150
150
|
if (event.type === "tool_call_delta") {
|
|
@@ -32,7 +32,7 @@ export interface JsonObject {
|
|
|
32
32
|
[key: string]: unknown;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
interface ParsedToolCall {
|
|
36
36
|
id: string;
|
|
37
37
|
name: string;
|
|
38
38
|
arguments: string;
|
|
@@ -57,7 +57,7 @@ export interface ChunkBase {
|
|
|
57
57
|
|
|
58
58
|
|
|
59
59
|
|
|
60
|
-
|
|
60
|
+
function isSurfaceInput(value: unknown): value is SurfaceInput {
|
|
61
61
|
return isRecord(value) && "body" in value && "path" in value && typeof value.path === "string";
|
|
62
62
|
}
|
|
63
63
|
|
|
@@ -75,7 +75,7 @@ export function requestBody(input: unknown): unknown {
|
|
|
75
75
|
return decodeBody(isSurfaceInput(input) ? input.body : input);
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
-
|
|
78
|
+
function nonNegativeNumber(value: unknown): number | undefined {
|
|
79
79
|
const number = asNumber(value);
|
|
80
80
|
return number !== undefined && number >= 0 ? number : undefined;
|
|
81
81
|
}
|
|
@@ -87,13 +87,13 @@ const AUDIO_FORMAT_MEDIA_TYPE: Readonly<Record<string, string>> = {
|
|
|
87
87
|
mpeg: "audio/mpeg",
|
|
88
88
|
};
|
|
89
89
|
|
|
90
|
-
|
|
90
|
+
function audioFormatToMediaType(format: string | undefined): string {
|
|
91
91
|
if (format === undefined || format.length === 0) return "audio/mpeg";
|
|
92
92
|
if (format.includes("/")) return format;
|
|
93
93
|
return AUDIO_FORMAT_MEDIA_TYPE[format.toLowerCase()] ?? "audio/mpeg";
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
-
|
|
96
|
+
function parseContentPart(value: unknown): ContentPart {
|
|
97
97
|
if (typeof value === "string") return textPart(value);
|
|
98
98
|
if (!isRecord(value)) {
|
|
99
99
|
throw new Error("Malformed content part: expected string or object");
|
|
@@ -173,7 +173,7 @@ export function parseContentPart(value: unknown): ContentPart {
|
|
|
173
173
|
throw new Error(`Malformed content part: unsupported type "${type ?? "unknown"}"`);
|
|
174
174
|
}
|
|
175
175
|
|
|
176
|
-
|
|
176
|
+
function parseContent(value: unknown): ContentPart[] {
|
|
177
177
|
if (typeof value === "string") return [textPart(value)];
|
|
178
178
|
if (!Array.isArray(value)) return [];
|
|
179
179
|
return value.map((part) => parseContentPart(part));
|
|
@@ -271,7 +271,7 @@ export function parseMessage(value: unknown): CanonicalMessage {
|
|
|
271
271
|
}
|
|
272
272
|
|
|
273
273
|
|
|
274
|
-
|
|
274
|
+
function addExtension(
|
|
275
275
|
controls: GenerationControls,
|
|
276
276
|
key: `extension:${string}`,
|
|
277
277
|
value: unknown,
|
|
@@ -9,7 +9,7 @@ import { usageToChatWire, type OpenAiUsage } from "../../providers/provider-usag
|
|
|
9
9
|
import { TEXT_ENCODER } from "../../protocol/sse-frame";
|
|
10
10
|
import { SurfaceStreamEncoder } from "./stream-frame";
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
interface CompletionEncodingOptions {
|
|
13
13
|
response_id?: string;
|
|
14
14
|
model?: string;
|
|
15
15
|
created?: number;
|
|
@@ -150,7 +150,7 @@ export class CompletionStreamEncoder extends SurfaceStreamEncoder<CanonicalEvent
|
|
|
150
150
|
}
|
|
151
151
|
}
|
|
152
152
|
|
|
153
|
-
|
|
153
|
+
class CompletionAdapter implements SurfaceAdapter {
|
|
154
154
|
readonly surface = "completion" as const;
|
|
155
155
|
|
|
156
156
|
matchesBodyShape(body: unknown): boolean {
|
|
@@ -12,7 +12,7 @@ export function textPart(text: string): ContentPart {
|
|
|
12
12
|
* `document.source`). Each parser reads its own shape and hands the extracted
|
|
13
13
|
* values here so the data-URI split happens once.
|
|
14
14
|
*/
|
|
15
|
-
|
|
15
|
+
interface RawFileBlock {
|
|
16
16
|
/** Inline payload — may be raw base64 or a whole `data:` URI. */
|
|
17
17
|
readonly data?: unknown;
|
|
18
18
|
readonly media_type?: string | undefined;
|
|
@@ -6,7 +6,7 @@ import { completeRequiredSchema } from "../../protocol/codecs";
|
|
|
6
6
|
import type { ToolDefinition } from "../../protocol/canonical-model";
|
|
7
7
|
|
|
8
8
|
/** Tool-definition dialects preserve each surface's distinct wire fields. */
|
|
9
|
-
|
|
9
|
+
type ToolDialect = "chat" | "responses" | "messages";
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Parses a tool definition from one wire dialect.
|
|
@@ -165,7 +165,7 @@ function classifyNativeToolType(type: string): Exclude<ToolDefinition["tool_type
|
|
|
165
165
|
|
|
166
166
|
export const CANONICAL_REASONING_EFFORTS: ReadonlySet<string> = new Set(REASONING_EFFORTS);
|
|
167
167
|
|
|
168
|
-
|
|
168
|
+
function isReasoningEffort(value: unknown): value is NonNullable<ReasoningIntent["effort"]> {
|
|
169
169
|
return typeof value === "string" && CANONICAL_REASONING_EFFORTS.has(value as NonNullable<ReasoningIntent["effort"]>);
|
|
170
170
|
}
|
|
171
171
|
|
|
@@ -53,7 +53,7 @@ export interface MessagesStreamEvent extends MessagesWireObject {
|
|
|
53
53
|
| "message_stop";
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
|
|
56
|
+
function contentArray(value: unknown, label: string): unknown[] {
|
|
57
57
|
if (stringValue(value)) return [value];
|
|
58
58
|
if (!Array.isArray(value)) throw new Error(`Messages ${label} must be a string or block array`);
|
|
59
59
|
return value;
|
|
@@ -165,7 +165,7 @@ export function parseBlock(value: unknown): ContentPart {
|
|
|
165
165
|
};
|
|
166
166
|
}
|
|
167
167
|
|
|
168
|
-
|
|
168
|
+
function parseBlocks(value: unknown, label: string): ContentPart[] {
|
|
169
169
|
return contentArray(value, label).map(parseBlock);
|
|
170
170
|
}
|
|
171
171
|
|
|
@@ -207,7 +207,7 @@ export function parseTools(value: unknown): ToolDefinition[] | undefined {
|
|
|
207
207
|
|
|
208
208
|
|
|
209
209
|
/** Reads `thinking.block_binding.prefix_mismatch_behavior` when present. */
|
|
210
|
-
|
|
210
|
+
function prefixMismatchBehavior(value: unknown): "drop_block" | "error" | undefined {
|
|
211
211
|
if (!isRecord(value)) return undefined;
|
|
212
212
|
const behavior = value["prefix_mismatch_behavior"];
|
|
213
213
|
if (behavior === "drop_block" || behavior === "error") return behavior;
|
|
@@ -51,12 +51,10 @@ export function reasoningSummaryHasBoundary(
|
|
|
51
51
|
* ends one item is the first half of a word the next item finishes. Inserting
|
|
52
52
|
* a break there painted `Gil` in the thinking pane and `iran` in the answer.
|
|
53
53
|
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
* carry no terminal punctuation (`**Crafting a brief reply**`), which glued
|
|
59
|
-
* separate thoughts into one line.
|
|
54
|
+
* A capital by itself is not a boundary: a sentence can continue in the next
|
|
55
|
+
* item, and inserting a paragraph there makes a captured chat look split. A
|
|
56
|
+
* sentence terminator or an explicit Markdown block marker is stronger
|
|
57
|
+
* evidence; otherwise a capitalized continuation gets only a space.
|
|
60
58
|
*/
|
|
61
59
|
export function reasoningSummarySeparator(
|
|
62
60
|
previous: ReasoningSummaryIdentity | undefined,
|
|
@@ -66,13 +64,10 @@ export function reasoningSummarySeparator(
|
|
|
66
64
|
): string {
|
|
67
65
|
if (!reasoningSummaryHasBoundary(previous, current)) return "";
|
|
68
66
|
if (/\s$/.test(previousText ?? "") || /^\s/.test(currentText)) return "";
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
if (/^[A-Z
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
// a sentence, the next one is a different artifact (the raw reasoning after a
|
|
75
|
-
// summary), and gluing them produced `file.raw private reasoning`.
|
|
76
|
-
if (/[.!?][)"'\]]*$/.test(previousText ?? "")) return " ";
|
|
67
|
+
if (/^[*#\-]/.test(currentText)) return "\n\n";
|
|
68
|
+
const previousEndedSentence = /[.!?][)"'\]]*$/.test(previousText ?? "");
|
|
69
|
+
if (previousEndedSentence && /^[A-ZÀ-ÖØ-Þ]/.test(currentText)) return "\n\n";
|
|
70
|
+
if (/^[A-ZÀ-ÖØ-Þ]/.test(currentText)) return " ";
|
|
71
|
+
if (previousEndedSentence) return " ";
|
|
77
72
|
return "";
|
|
78
73
|
}
|
|
@@ -7,7 +7,7 @@ import { hasOwn, parseResponsesRequest, unwrapBody } from "./parse";
|
|
|
7
7
|
import { encodeResponsesEvents, encodeResponsesWireEvents } from "./encode";
|
|
8
8
|
|
|
9
9
|
/** The OpenAI Responses public surface adapter. */
|
|
10
|
-
|
|
10
|
+
class ResponsesAdapter implements SurfaceAdapter {
|
|
11
11
|
readonly surface = "responses" as const;
|
|
12
12
|
|
|
13
13
|
/** Check only top-level Responses shape markers, never arbitrary prompt text. */
|
|
@@ -1,15 +1,6 @@
|
|
|
1
1
|
import { GatewayError } from "../../../protocol/gateway-error";
|
|
2
2
|
|
|
3
3
|
/** Typed failures for the OpenAI Responses surface. */
|
|
4
|
-
export class ResponsesReasoningError extends GatewayError {
|
|
5
|
-
readonly field: "include" | "store" | "reasoning.encrypted_content";
|
|
6
|
-
|
|
7
|
-
constructor(field: "include" | "store" | "reasoning.encrypted_content", message: string) {
|
|
8
|
-
super("capability_unsupported", 400, message, { field });
|
|
9
|
-
this.name = "ResponsesReasoningError";
|
|
10
|
-
this.field = field;
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
4
|
|
|
14
5
|
/** Stable typed failure for malformed canonical sequence numbers. */
|
|
15
6
|
export class ResponsesSequenceError extends GatewayError {
|