cartethyia 2.7.4 → 2.7.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +2 -0
- package/README.md +17 -5
- package/dist/dashboard/assets/{BannedPage-ChG5svFb.js → BannedPage-Cj15GsMp.js} +1 -1
- package/dist/dashboard/assets/BannedPage-Cj15GsMp.js.gz +0 -0
- package/dist/dashboard/assets/ClaimGiftsButton-D83pBJ6J.js +1 -0
- package/dist/dashboard/assets/ClaimGiftsButton-D83pBJ6J.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolDetailPage-BIcWQun7.js → CliToolDetailPage-DZKmVuJN.js} +1 -1
- package/dist/dashboard/assets/CliToolDetailPage-DZKmVuJN.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolsPage-Bf2VelDe.js → CliToolsPage-BIccZmeL.js} +1 -1
- package/dist/dashboard/assets/CliToolsPage-BIccZmeL.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-DEEKkYD-.js +1 -0
- package/dist/dashboard/assets/CombosPage-DEEKkYD-.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-D5rol2j2.js +1 -0
- package/dist/dashboard/assets/ConsoleLogPage-D5rol2j2.js.gz +0 -0
- package/dist/dashboard/assets/{CustomizationPage-ggeMSjte.js → CustomizationPage-BKj_r0rp.js} +1 -1
- package/dist/dashboard/assets/CustomizationPage-BKj_r0rp.js.gz +0 -0
- package/dist/dashboard/assets/InjectSystemPromptPanel-B5nZvmf0.js +656 -0
- package/dist/dashboard/assets/InjectSystemPromptPanel-B5nZvmf0.js.gz +0 -0
- package/dist/dashboard/assets/{LoginPage-U6AKaJcF.js → LoginPage-DkXb6dx-.js} +1 -1
- package/dist/dashboard/assets/LoginPage-DkXb6dx-.js.gz +0 -0
- package/dist/dashboard/assets/{ModelPicker-BMS2t8BA.js → ModelPicker-DnQPSIA8.js} +1 -1
- package/dist/dashboard/assets/ModelPicker-DnQPSIA8.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-Ci2wQKUz.js +1 -0
- package/dist/dashboard/assets/OverviewPage-Ci2wQKUz.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-odlgYrOg.js +5 -0
- package/dist/dashboard/assets/ProviderDetailPage-odlgYrOg.js.gz +0 -0
- package/dist/dashboard/assets/ProviderIcon-EfeloBn4.js +1 -0
- package/dist/dashboard/assets/ProviderIcon-EfeloBn4.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-CqFBQEB8.js +1 -0
- package/dist/dashboard/assets/ProvidersPage-CqFBQEB8.js.gz +0 -0
- package/dist/dashboard/assets/{ProxyPage-lsrx7NYg.js → ProxyPage-5SnUSy3j.js} +1 -1
- package/dist/dashboard/assets/ProxyPage-5SnUSy3j.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-CmnMDzhP.js +1 -0
- package/dist/dashboard/assets/QuotaPage-CmnMDzhP.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-BJtRZ4qD.js +1 -0
- package/dist/dashboard/assets/SettingsPage-BJtRZ4qD.js.gz +0 -0
- package/dist/dashboard/assets/{SetupPage-f0DE1h0-.js → SetupPage-WTa0sktC.js} +1 -1
- package/dist/dashboard/assets/SetupPage-WTa0sktC.js.gz +0 -0
- package/dist/dashboard/assets/{StudioPage-Cq5tW7hW.js → StudioPage-BfAb5-o_.js} +3 -3
- package/dist/dashboard/assets/StudioPage-BfAb5-o_.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-BNzeZ2Vk.js +56 -0
- package/dist/dashboard/assets/UsagePage-BNzeZ2Vk.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-l29JROPv.js +1 -0
- package/dist/dashboard/assets/api-keys-l29JROPv.js.gz +0 -0
- package/dist/dashboard/assets/app--PJWq8_C.css +2 -0
- package/dist/dashboard/assets/app--PJWq8_C.css.gz +0 -0
- package/dist/dashboard/assets/assert-C0K-c4eo.js +1 -0
- package/dist/dashboard/assets/assert-C0K-c4eo.js.gz +0 -0
- package/dist/dashboard/assets/{badge-D9CS6dZk.js → badge-DfbhQU4k.js} +1 -1
- package/dist/dashboard/assets/badge-DfbhQU4k.js.gz +0 -0
- package/dist/dashboard/assets/{banner-DympBNq4.js → banner-BllT4LcF.js} +1 -1
- package/dist/dashboard/assets/banner-BllT4LcF.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-CtkX66Wd.js} +1 -1
- package/dist/dashboard/assets/card-CtkX66Wd.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/cli-tools-CQXVMn4P.js +1 -0
- package/dist/dashboard/assets/cli-tools-CQXVMn4P.js.gz +0 -0
- package/dist/dashboard/assets/{clipboard-button-DJm-9pz7.js → clipboard-button-BQqBQd2n.js} +1 -1
- package/dist/dashboard/assets/clipboard-button-BQqBQd2n.js.gz +0 -0
- package/dist/dashboard/assets/{dialog-8fiE6tec.js → dialog-BEK7KP0H.js} +1 -1
- package/dist/dashboard/assets/dialog-BEK7KP0H.js.gz +0 -0
- package/dist/dashboard/assets/{drawer-CqGYeOrd.js → drawer-BrTEZ8zu.js} +1 -1
- package/dist/dashboard/assets/drawer-BrTEZ8zu.js.gz +0 -0
- package/dist/dashboard/assets/entry-CHWxbS56.js +1 -0
- package/dist/dashboard/assets/entry-CHWxbS56.js.gz +0 -0
- package/dist/dashboard/assets/{entry-BSAsYsmV.js → entry-EBHJ3DrE.js} +1 -1
- package/dist/dashboard/assets/entry-EBHJ3DrE.js.gz +0 -0
- package/dist/dashboard/assets/entry-LCS85hiq.js +4 -0
- package/dist/dashboard/assets/entry-LCS85hiq.js.gz +0 -0
- package/dist/dashboard/assets/{formatters-D-MTJxMr.js → formatters-BAACA727.js} +1 -1
- package/dist/dashboard/assets/formatters-BAACA727.js.gz +0 -0
- package/dist/dashboard/assets/{github-badge-CPK47xQx.js → github-badge-YSCE4UC2.js} +1 -1
- package/dist/dashboard/assets/github-badge-YSCE4UC2.js.gz +0 -0
- package/dist/dashboard/assets/{index-DBNPOfhV.js → index-FQlj0HUT.js} +2 -2
- package/dist/dashboard/assets/index-FQlj0HUT.js.gz +0 -0
- package/dist/dashboard/assets/{inline-BT0zntEr.js → inline-C4gU5iOL.js} +1 -1
- package/dist/dashboard/assets/inline-C4gU5iOL.js.gz +0 -0
- package/dist/dashboard/assets/{input-Da2ZViWg.js → input-BHHX_Tmm.js} +1 -1
- package/dist/dashboard/assets/input-BHHX_Tmm.js.gz +0 -0
- package/dist/dashboard/assets/{layout-BXNeLw6M.js → layout-CajrYUnD.js} +1 -1
- package/dist/dashboard/assets/layout-CajrYUnD.js.gz +0 -0
- package/dist/dashboard/assets/{list-row-BycOrrIV.js → list-row-Bx2I7XUe.js} +1 -1
- package/dist/dashboard/assets/list-row-Bx2I7XUe.js.gz +0 -0
- package/dist/dashboard/assets/live-DNNqRbj2.js +1 -0
- package/dist/dashboard/assets/live-DNNqRbj2.js.gz +0 -0
- package/dist/dashboard/assets/network-C5UBACrJ.js +1 -0
- package/dist/dashboard/assets/network-C5UBACrJ.js.gz +0 -0
- package/dist/dashboard/assets/numbers-CUWAZK4J.js +2 -0
- package/dist/dashboard/assets/numbers-CUWAZK4J.js.gz +0 -0
- package/dist/dashboard/assets/pagination-Xsi9Qx04.js +1 -0
- package/dist/dashboard/assets/pagination-Xsi9Qx04.js.gz +0 -0
- package/dist/dashboard/assets/quota-DvQbD_sr.js +1 -0
- package/dist/dashboard/assets/quota-DvQbD_sr.js.gz +0 -0
- package/dist/dashboard/assets/routing-in0f_IfK.js +1 -0
- package/dist/dashboard/assets/routing-in0f_IfK.js.gz +0 -0
- package/dist/dashboard/assets/{select-BIYIBkMU.js → select-BLULNqTp.js} +1 -1
- package/dist/dashboard/assets/select-BLULNqTp.js.gz +0 -0
- package/dist/dashboard/assets/settings-CcGczj6I.js +1 -0
- package/dist/dashboard/assets/settings-CcGczj6I.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-D2lIR0tg.js} +1 -1
- package/dist/dashboard/assets/stack-D2lIR0tg.js.gz +0 -0
- package/dist/dashboard/assets/{state-CkfdYWel.js → state-DTARjfiW.js} +1 -1
- package/dist/dashboard/assets/state-DTARjfiW.js.gz +0 -0
- package/dist/dashboard/assets/{switch-SYm237HG.js → switch-c0oPSnqW.js} +1 -1
- package/dist/dashboard/assets/switch-c0oPSnqW.js.gz +0 -0
- package/dist/dashboard/assets/{useMutation-BQqCRNIi.js → useMutation-m8WGNPrz.js} +1 -1
- package/dist/dashboard/assets/useMutation-m8WGNPrz.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 +56 -55
- 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/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/package.json +4 -6
- package/scripts/cli/update.ts +15 -41
- package/src/boot/app.ts +58 -2
- package/src/boot/dependencies.ts +786 -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 +13 -14
- package/src/config/resources.ts +0 -5
- package/src/config/runtime.ts +2 -5
- package/src/config/spec.ts +26 -6
- 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 +48 -12
- 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 +7 -7
- package/src/console/cli-tools/injectors/cline.ts +110 -110
- package/src/console/cli-tools/injectors/codex.ts +181 -181
- package/src/console/cli-tools/injectors/copilot.ts +108 -108
- package/src/console/cli-tools/injectors/driver.ts +247 -247
- package/src/console/cli-tools/injectors/jcode.ts +115 -115
- package/src/console/cli-tools/injectors/openclaw.ts +161 -161
- package/src/console/cli-tools/injectors/opencode.ts +153 -153
- package/src/console/cli-tools/routes.ts +1 -1
- package/src/console/cli-tools/service.ts +1 -1
- 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 +1 -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 +20 -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 +4 -4
- 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 +2 -2
- package/src/observability/event-query.ts +59 -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 +12 -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 +113 -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 +10 -1
- 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 +3 -49
- 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 +42 -3
- 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 +23 -35
- 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 +0 -14
- package/src/providers/integrations/antigravity/antigravity.ts +2 -2
- 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 +38 -5
- 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 +55 -10
- package/src/providers/integrations/github/github-oauth.ts +6 -5
- package/src/providers/integrations/github/github.ts +41 -14
- 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 +81 -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 +85 -8
- 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 +354 -62
- 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 +53 -10
- 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 +131 -116
- package/src/providers/operations/account-health-service.ts +31 -39
- package/src/providers/operations/account-reset-service.ts +58 -1
- 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 +81 -16
- 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 +2 -2
- package/src/security/admission/ttl.ts +61 -61
- package/src/security/api-key-auth.ts +16 -4
- 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 +1 -1
- 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 +6 -0
- package/src/transport/guards/auth-guard.ts +0 -6
- package/src/transport/guards/body-policy.ts +3 -3
- 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 +1 -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 +152 -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/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 +376 -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.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-BIcWQun7.js.gz +0 -0
- 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.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.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.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/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-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.gz +0 -0
- package/dist/dashboard/assets/drawer-CqGYeOrd.js.gz +0 -0
- 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.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/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/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/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/{app-BayTTy01.js → app-Bgd3zANQ.js} +0 -0
- /package/dist/dashboard/assets/{app-BayTTy01.js.gz → app-Bgd3zANQ.js.gz} +0 -0
- /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
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import type { RouteCandidate, RouteSnapshot } from "./route-model";
|
|
3
3
|
|
|
4
4
|
/** One parsed model reference: `provider/model` or a bare `model`. */
|
|
5
|
-
|
|
5
|
+
interface ParsedModel {
|
|
6
6
|
readonly providerId: string | null;
|
|
7
7
|
readonly modelId: string;
|
|
8
8
|
}
|
|
@@ -24,7 +24,7 @@ export function parseModelReference(model: string): ParsedModel {
|
|
|
24
24
|
* candidates cost K×C `candidateMatches` calls (each re-parsing the model
|
|
25
25
|
* string), repeated on every request.
|
|
26
26
|
*/
|
|
27
|
-
|
|
27
|
+
interface RouteCandidateIndex {
|
|
28
28
|
/** Candidates keyed by `provider_id/model_id`, snapshot order preserved. */
|
|
29
29
|
readonly qualified: ReadonlyMap<string, readonly RouteCandidate[]>;
|
|
30
30
|
/** Candidates keyed by bare `model_id`, snapshot order preserved. */
|
|
@@ -23,7 +23,7 @@ export class RoundRobinState {
|
|
|
23
23
|
* combinations would retain one entry forever. 1000 is far above the working
|
|
24
24
|
* set of any real catalog, so eviction only ever targets genuinely cold keys.
|
|
25
25
|
*/
|
|
26
|
-
|
|
26
|
+
const MAX_ROUND_ROBIN_ENTRIES = 1000;
|
|
27
27
|
|
|
28
28
|
/**
|
|
29
29
|
* Creates the bounded LRU map the router keeps round-robin state in.
|
|
@@ -20,12 +20,12 @@ import {
|
|
|
20
20
|
import { DEFAULT_PROVIDER_MAX_INFLIGHT } from "../../persistence/schema";
|
|
21
21
|
import { resolveTenantOverride } from "../../persistence/tenant-scope";
|
|
22
22
|
import {
|
|
23
|
-
type RouteCandidate,
|
|
24
23
|
type ComboDefinition,
|
|
25
24
|
type PoolRoutingSetting,
|
|
26
25
|
type ProviderRoutingMap,
|
|
27
26
|
type RouteSnapshot,
|
|
28
27
|
type SnapshotBuilder,
|
|
28
|
+
type RoutedCandidate,
|
|
29
29
|
} from "./route-model";
|
|
30
30
|
import { DEFAULT_PROXY_SKIP_PROVIDER_IDS } from "../../providers/provider-metadata";
|
|
31
31
|
import { buildClaudeCliUserAgent } from "../../providers/operations/custom-cli-headers";
|
|
@@ -84,18 +84,6 @@ export function cliMappingSourceKeys(toolId: string, sourceModel: string): reado
|
|
|
84
84
|
* — `RoutingEngine.plan()` filters candidates by the requesting tenant.
|
|
85
85
|
*/
|
|
86
86
|
|
|
87
|
-
type RouteCandidateWithHealth = RouteCandidate & {
|
|
88
|
-
health_status?: "cooldown" | "model_cooldown" | "disabled";
|
|
89
|
-
cooldown_kind?: "hard" | "soft";
|
|
90
|
-
/**
|
|
91
|
-
* Epoch milliseconds at which this candidate's cooling window ends, when one
|
|
92
|
-
* is in force. Routing-internal: it is what lets `plan()` report a real reset
|
|
93
|
-
* on the all-accounts-cooling answer, and it never reaches a client verbatim —
|
|
94
|
-
* only the derived `retry-after` does.
|
|
95
|
-
*/
|
|
96
|
-
cooldown_until?: number;
|
|
97
|
-
};
|
|
98
|
-
|
|
99
87
|
const HARD_COOLDOWN_CATEGORIES: Readonly<Record<string, true>> = {
|
|
100
88
|
quota_exhausted: true,
|
|
101
89
|
policy_blocked: true,
|
|
@@ -246,6 +234,7 @@ interface MergedModelRow {
|
|
|
246
234
|
providerId: string;
|
|
247
235
|
modelId: string;
|
|
248
236
|
wireFamily: WireFamily;
|
|
237
|
+
dialect?: string | null;
|
|
249
238
|
serviceKind: ServiceKind;
|
|
250
239
|
endpointPath: string;
|
|
251
240
|
modalities: unknown;
|
|
@@ -270,6 +259,7 @@ function mergeModelCatalog(
|
|
|
270
259
|
providerId: row.providerId,
|
|
271
260
|
modelId: row.modelId,
|
|
272
261
|
wireFamily: row.wireFamily as WireFamily,
|
|
262
|
+
...(row.dialect === null ? {} : { dialect: row.dialect }),
|
|
273
263
|
serviceKind: row.serviceKind as ServiceKind,
|
|
274
264
|
endpointPath: row.endpointPath,
|
|
275
265
|
modalities: row.modalities,
|
|
@@ -309,6 +299,7 @@ const MODEL_COLUMNS = {
|
|
|
309
299
|
providerId: models.providerId,
|
|
310
300
|
modelId: models.modelId,
|
|
311
301
|
wireFamily: models.wireFamily,
|
|
302
|
+
dialect: models.dialect,
|
|
312
303
|
serviceKind: models.serviceKind,
|
|
313
304
|
endpointPath: models.endpointPath,
|
|
314
305
|
modalities: models.modalities,
|
|
@@ -678,7 +669,7 @@ class RouteCatalogRepository {
|
|
|
678
669
|
accountsByProvider.set(account.providerId, list);
|
|
679
670
|
}
|
|
680
671
|
|
|
681
|
-
const candidates:
|
|
672
|
+
const candidates: RoutedCandidate[] = [];
|
|
682
673
|
for (const model of mergedModelRows) {
|
|
683
674
|
const providerTenantId = providerTenantById.get(model.providerId);
|
|
684
675
|
if (providerTenantId === undefined) continue;
|
|
@@ -717,15 +708,17 @@ class RouteCatalogRepository {
|
|
|
717
708
|
const tenantPools = resolveNetworkPools(model.providerId, tenantOwnerId);
|
|
718
709
|
if (!tenantPools) continue;
|
|
719
710
|
emittedPoolAccount = true;
|
|
720
|
-
const tenantCandidate:
|
|
711
|
+
const tenantCandidate: RoutedCandidate = {
|
|
721
712
|
provider_id: model.providerId,
|
|
722
713
|
model_id: model.modelId,
|
|
723
714
|
wire_family: model.wireFamily as WireFamily,
|
|
715
|
+
...(model.dialect == null ? {} : { dialect: model.dialect }),
|
|
724
716
|
service_kind: model.serviceKind,
|
|
725
717
|
endpoint: model.endpointPath,
|
|
726
718
|
...(model.outputLimit == null ? {} : { output_limit: model.outputLimit }),
|
|
727
719
|
capability_profile: capabilityProfile,
|
|
728
720
|
tenant_id: tenantOwnerId,
|
|
721
|
+
...(model.freeTier === true ? { free_tier: true } : {}),
|
|
729
722
|
// Public credential-less candidates have no account to own them.
|
|
730
723
|
owner_tenant_id: null,
|
|
731
724
|
requires_account: false as const,
|
|
@@ -756,15 +749,17 @@ class RouteCatalogRepository {
|
|
|
756
749
|
for (const tId of configuredPoolTenants) {
|
|
757
750
|
const tenantPools = resolveNetworkPools(model.providerId, tId);
|
|
758
751
|
if (tenantPools) {
|
|
759
|
-
const tenantCandidate:
|
|
752
|
+
const tenantCandidate: RoutedCandidate = {
|
|
760
753
|
provider_id: model.providerId,
|
|
761
754
|
model_id: model.modelId,
|
|
762
755
|
wire_family: model.wireFamily as WireFamily,
|
|
756
|
+
...(model.dialect == null ? {} : { dialect: model.dialect }),
|
|
763
757
|
service_kind: model.serviceKind,
|
|
764
758
|
endpoint: model.endpointPath,
|
|
765
759
|
...(model.outputLimit == null ? {} : { output_limit: model.outputLimit }),
|
|
766
760
|
capability_profile: capabilityProfile,
|
|
767
761
|
tenant_id: tId,
|
|
762
|
+
...(model.freeTier === true ? { free_tier: true } : {}),
|
|
768
763
|
// Public credential-less candidates have no account to own them.
|
|
769
764
|
owner_tenant_id: null,
|
|
770
765
|
requires_account: false as const,
|
|
@@ -784,15 +779,17 @@ class RouteCatalogRepository {
|
|
|
784
779
|
const networkPools = providerRequiresAccount
|
|
785
780
|
? undefined
|
|
786
781
|
: resolveNetworkPools(model.providerId, providerTenantId);
|
|
787
|
-
const candidate:
|
|
782
|
+
const candidate: RoutedCandidate = {
|
|
788
783
|
provider_id: model.providerId,
|
|
789
784
|
model_id: model.modelId,
|
|
790
785
|
wire_family: model.wireFamily as WireFamily,
|
|
786
|
+
...(model.dialect == null ? {} : { dialect: model.dialect }),
|
|
791
787
|
service_kind: model.serviceKind,
|
|
792
788
|
endpoint: model.endpointPath,
|
|
793
789
|
...(model.outputLimit == null ? {} : { output_limit: model.outputLimit }),
|
|
794
790
|
capability_profile: capabilityProfile,
|
|
795
791
|
tenant_id: providerTenantId,
|
|
792
|
+
...(model.freeTier === true ? { free_tier: true } : {}),
|
|
796
793
|
// Public credential-less candidates have no account to own them.
|
|
797
794
|
owner_tenant_id: null,
|
|
798
795
|
...(providerRequiresAccount
|
|
@@ -826,15 +823,17 @@ class RouteCatalogRepository {
|
|
|
826
823
|
}
|
|
827
824
|
const networkPools = resolveNetworkPools(model.providerId, rowTenantId);
|
|
828
825
|
const routeUserAgent = resolveUserAgent(model.providerId, rowTenantId);
|
|
829
|
-
const candidate:
|
|
826
|
+
const candidate: RoutedCandidate = {
|
|
830
827
|
provider_id: model.providerId,
|
|
831
828
|
model_id: model.modelId,
|
|
832
829
|
wire_family: model.wireFamily as WireFamily,
|
|
830
|
+
...(model.dialect == null ? {} : { dialect: model.dialect }),
|
|
833
831
|
service_kind: model.serviceKind,
|
|
834
832
|
endpoint: model.endpointPath,
|
|
835
833
|
...(model.outputLimit == null ? {} : { output_limit: model.outputLimit }),
|
|
836
834
|
capability_profile: capabilityProfile,
|
|
837
835
|
...(routeUserAgent === undefined ? {} : { user_agent: routeUserAgent }),
|
|
836
|
+
...(model.freeTier === true ? { free_tier: true } : {}),
|
|
838
837
|
tenant_id: rowTenantId,
|
|
839
838
|
provider_account_id: account.id,
|
|
840
839
|
// The account's owning tenant (null = shared pool-wide) — the bucket
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
import { GatewayError } from "../../protocol/gateway-error";
|
|
3
3
|
import { type ServiceKind, type WireFamily } from "../../protocol/canonical-model";
|
|
4
4
|
import type { CallerIdentity } from "../../security/access-control";
|
|
5
|
+
import type { ProviderQuotaWindow } from "../../providers/quota/quota-contracts";
|
|
6
|
+
import { log } from "../../observability/logger";
|
|
5
7
|
|
|
6
8
|
export type { CallerIdentity };
|
|
7
9
|
|
|
@@ -14,6 +16,8 @@ export interface RouteCandidate {
|
|
|
14
16
|
readonly provider_id: string;
|
|
15
17
|
readonly model_id: string;
|
|
16
18
|
readonly wire_family: WireFamily;
|
|
19
|
+
/** Explicit wire dialect (from `models.dialect`), when set. Absent = resolver fallback. */
|
|
20
|
+
readonly dialect?: string;
|
|
17
21
|
/**
|
|
18
22
|
* Protocol shape of the model. `llm` (the default) flows through the
|
|
19
23
|
* canonical surface pipeline; a non-`llm` model is dispatched by its native
|
|
@@ -84,6 +88,12 @@ export interface RouteCandidate {
|
|
|
84
88
|
* refused.
|
|
85
89
|
*/
|
|
86
90
|
readonly pro_only?: boolean;
|
|
91
|
+
/**
|
|
92
|
+
* True when the provider publishes this model as free (its own directory
|
|
93
|
+
* statement, e.g. Qoder's `is_free`). A free model may be routed to any
|
|
94
|
+
* account — the credit-preference ordering skips it entirely.
|
|
95
|
+
*/
|
|
96
|
+
readonly free_tier?: boolean;
|
|
87
97
|
/**
|
|
88
98
|
* The plan the provider last reported for `provider_account_id`.
|
|
89
99
|
*
|
|
@@ -95,9 +105,44 @@ export interface RouteCandidate {
|
|
|
95
105
|
readonly usage_floor?: number;
|
|
96
106
|
readonly usage_floor_unit?: "credit" | "percent";
|
|
97
107
|
/** Live quota windows (cache) the floor check evaluates. */
|
|
98
|
-
readonly quota_windows?: readonly
|
|
108
|
+
readonly quota_windows?: readonly ProviderQuotaWindow[];
|
|
109
|
+
/** A successful Start Plan balance authoritatively lists its model grants. */
|
|
110
|
+
readonly quota_entitlements_known?: boolean;
|
|
99
111
|
}
|
|
100
112
|
|
|
113
|
+
/**
|
|
114
|
+
* A candidate as the routing engine actually evaluates it: the catalog fields
|
|
115
|
+
* above plus the health/cooling stamp `route-catalog.ts` projects onto it and
|
|
116
|
+
* the plan-time slow-lane marker.
|
|
117
|
+
*
|
|
118
|
+
* Declared here — not as a route-catalog-local intersection — so
|
|
119
|
+
* `EligibilityEvaluator.evaluate` can take the routed shape without a cast,
|
|
120
|
+
* and so every stamping site agrees on the same field set by construction.
|
|
121
|
+
*/
|
|
122
|
+
export type RoutedCandidate = RouteCandidate & {
|
|
123
|
+
/**
|
|
124
|
+
* Health marker the catalog projects from `provider_accounts.status` and
|
|
125
|
+
* unexpired `model_cooldowns` entries. Absent = no health exclusion known.
|
|
126
|
+
* Mutable by declaration: the catalog builds the base candidate first and
|
|
127
|
+
* stamps health/cooling onto it as account rows are walked.
|
|
128
|
+
*/
|
|
129
|
+
health_status?: "cooldown" | "model_cooldown" | "disabled";
|
|
130
|
+
/** Cooldown class the catalog derives from the account's last error category. */
|
|
131
|
+
cooldown_kind?: "hard" | "soft";
|
|
132
|
+
/**
|
|
133
|
+
* Epoch milliseconds at which this candidate's cooling window ends, when one
|
|
134
|
+
* is in force. Routing-internal: it is what lets `plan()` report a real reset
|
|
135
|
+
* on the all-accounts-cooling answer, and it never reaches a client verbatim —
|
|
136
|
+
* only the derived `retry-after` does.
|
|
137
|
+
*/
|
|
138
|
+
cooldown_until?: number;
|
|
139
|
+
/**
|
|
140
|
+
* Slow-lane stamp applied at plan time (test-supported; runtime slow-lane
|
|
141
|
+
* activity is read live via `sharedClaudeSlowLanes.isActive`).
|
|
142
|
+
*/
|
|
143
|
+
lane?: "slow";
|
|
144
|
+
};
|
|
145
|
+
|
|
101
146
|
/**
|
|
102
147
|
* Provider-routing strategies. The runtime tuple is the source: the console's
|
|
103
148
|
* Elysia body schema builds its `t.Literal` union from these values and its
|
|
@@ -270,7 +315,6 @@ export type EligibilityReason =
|
|
|
270
315
|
| "cooldown_hard"
|
|
271
316
|
| "model_cooldown"
|
|
272
317
|
| "quota_exhausted"
|
|
273
|
-
| "locked"
|
|
274
318
|
| "disabled"
|
|
275
319
|
|
|
276
320
|
export interface EligibilityDecision {
|
|
@@ -280,7 +324,7 @@ export interface EligibilityDecision {
|
|
|
280
324
|
/**
|
|
281
325
|
* Epoch milliseconds at which this candidate's cooling window ends, when the
|
|
282
326
|
* exclusion that produced this decision carries one. Absent for a healthy
|
|
283
|
-
* candidate and for every reason with no deadline (`disabled`,
|
|
327
|
+
* candidate and for every reason with no deadline (`disabled`,
|
|
284
328
|
* `quota_exhausted` from spent windows or the usage floor). `plan()` takes the
|
|
285
329
|
* minimum across the cooling set to report the earliest reset a client could
|
|
286
330
|
* retry against.
|
|
@@ -328,7 +372,7 @@ export interface AdmissionController {
|
|
|
328
372
|
* Translates the evaluator's internal reason codes into operator English.
|
|
329
373
|
*
|
|
330
374
|
* These names are the vocabulary the routing layers use with each other —
|
|
331
|
-
* `model_cooldown`, `cooldown_hard
|
|
375
|
+
* `model_cooldown`, `cooldown_hard` — not something to print into the
|
|
332
376
|
* public message, where the client would read an enum instead of an
|
|
333
377
|
* explanation. The codes stay in the envelope's `reasons` field for anyone who
|
|
334
378
|
* wants them; the sentence carries what they mean.
|
|
@@ -342,9 +386,9 @@ const ELIGIBILITY_REASON_LABELS: Readonly<Record<string, string>> = {
|
|
|
342
386
|
cooldown_hard: "every account is in a hard cooldown",
|
|
343
387
|
cooldown: "accounts are cooling down",
|
|
344
388
|
disabled: "all accounts are disabled",
|
|
345
|
-
locked: "all accounts are locked by another request",
|
|
346
389
|
credit_floor_reached: "credit or quota is exhausted",
|
|
347
390
|
quota_exhausted: "every account has an exhausted quota window or is below its configured usage floor",
|
|
391
|
+
capability_unsupported: "the accounts serving this model do not support the request's capabilities (e.g. images, tools, or reasoning)",
|
|
348
392
|
};
|
|
349
393
|
|
|
350
394
|
function explainEligibilityReasons(reasons: readonly string[]): string {
|
|
@@ -388,15 +432,6 @@ export function modelNotFoundError(requested: string, resolved: readonly string[
|
|
|
388
432
|
);
|
|
389
433
|
}
|
|
390
434
|
|
|
391
|
-
export function capabilityUnsupportedError(capability: string): GatewayError {
|
|
392
|
-
return new GatewayError(
|
|
393
|
-
"capability_unsupported",
|
|
394
|
-
400,
|
|
395
|
-
`No account serving this model supports ${capability} (request {requestId}). Drop the ${capability} part of the request, or route to a model that accepts it. Know your tools — this one just isn't in the box 🧰`,
|
|
396
|
-
{ capability },
|
|
397
|
-
);
|
|
398
|
-
}
|
|
399
|
-
|
|
400
435
|
/**
|
|
401
436
|
* Every candidate in the plan refused admission: each account is already
|
|
402
437
|
* serving its configured max-inflight.
|
|
@@ -439,7 +474,7 @@ export function capacityExhaustedError(context?: {
|
|
|
439
474
|
|
|
440
475
|
/**
|
|
441
476
|
* The model exists and is routed, but every account serving it is currently
|
|
442
|
-
* unusable (disabled
|
|
477
|
+
* unusable (disabled). This is transient and
|
|
443
478
|
* retry-able, so it must NOT surface as `model_not_found` — a 404 tells the
|
|
444
479
|
* client to fix its request when the real fix is to wait or add capacity.
|
|
445
480
|
*
|
|
@@ -453,10 +488,18 @@ export function accountsUnavailableError(
|
|
|
453
488
|
reasons: readonly string[],
|
|
454
489
|
): GatewayError {
|
|
455
490
|
const distinct = [...new Set(reasons)].sort();
|
|
491
|
+
const capabilityBlocked = distinct.includes("capability_unsupported");
|
|
492
|
+
// When the blocker is a capability (the model exists and accounts exist, but
|
|
493
|
+
// none can carry the request's images/tools/reasoning), telling the client to
|
|
494
|
+
// "add an account" or wait out a coffee break is misleading — more accounts
|
|
495
|
+
// cannot conjure a capability the model itself lacks. Name the real fix.
|
|
496
|
+
const hint = capabilityBlocked
|
|
497
|
+
? "Request a model that supports this capability (or a vision-capable model for images), or drop the unsupported part of the request. The model is the limit, not the pool 🧰"
|
|
498
|
+
: "Add an account, or wait for an operator to re-enable one. Every account is on a coffee break — try later ☕️";
|
|
456
499
|
return new GatewayError(
|
|
457
500
|
"accounts_unavailable",
|
|
458
501
|
503,
|
|
459
|
-
`No account in the pool can serve '${requested}' right now (request {requestId}) (${explainEligibilityReasons(distinct)}).
|
|
502
|
+
`No account in the pool can serve '${requested}' right now (request {requestId}) (${explainEligibilityReasons(distinct)}). ${hint}`,
|
|
460
503
|
{ model: requested, reasons: distinct, candidate_count: reasons.length, gateway_hint: "no_healthy_account" },
|
|
461
504
|
);
|
|
462
505
|
}
|
|
@@ -536,42 +579,83 @@ export class InMemoryRouteSnapshotService implements RouteSnapshotService {
|
|
|
536
579
|
* only populate the cache if no mutation happened while it ran.
|
|
537
580
|
*/
|
|
538
581
|
private building: { revision: RoutingRevision; promise: Promise<RouteSnapshot> } | undefined;
|
|
539
|
-
|
|
540
|
-
|
|
582
|
+
/**
|
|
583
|
+
* Debounced background-rebuild timer (G-02). One invalidate schedules a
|
|
584
|
+
* rebuild; a burst of invalidates within `rebuildDebounceMs` coalesces into
|
|
585
|
+
* the single latest-revision rebuild. The timer is cleared on `stop()`.
|
|
586
|
+
*/
|
|
587
|
+
private rebuildTimer: ReturnType<typeof setTimeout> | undefined;
|
|
588
|
+
private stopped = false;
|
|
589
|
+
|
|
590
|
+
constructor(
|
|
591
|
+
private readonly builder: SnapshotBuilder,
|
|
592
|
+
options: { readonly rebuildDebounceMs?: number } = {},
|
|
593
|
+
) {
|
|
594
|
+
this.rebuildDebounceMs = options.rebuildDebounceMs ?? 500;
|
|
541
595
|
// Peers' mutations arrive once per process, not per instance: the handler
|
|
542
|
-
//
|
|
596
|
+
// schedules a background rebuild, which is the only one there is.
|
|
543
597
|
this.unsubscribe = subscribeRoutingInvalidation(() => {
|
|
544
598
|
this.revision += 1;
|
|
545
|
-
this.
|
|
599
|
+
this.scheduleRebuild();
|
|
546
600
|
});
|
|
547
601
|
}
|
|
548
602
|
|
|
549
|
-
/** Ends the peer-invalidation subscription. */
|
|
603
|
+
/** Ends the peer-invalidation subscription and clears the rebuild timer. */
|
|
550
604
|
readonly unsubscribe: () => void;
|
|
551
605
|
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
606
|
+
/** Wall-clock debounce for coalescing invalidate bursts. */
|
|
607
|
+
private readonly rebuildDebounceMs: number;
|
|
608
|
+
|
|
609
|
+
/**
|
|
610
|
+
* Schedules a background rebuild at the current revision. Debounced: while a
|
|
611
|
+
* timer is pending, further invalidates just bump the revision and the
|
|
612
|
+
* pending rebuild picks up the latest. Single-flight by construction — the
|
|
613
|
+
* rebuild itself reuses {@link building}, so concurrent callers share one
|
|
614
|
+
* promise for the same revision.
|
|
615
|
+
*/
|
|
616
|
+
private scheduleRebuild(): void {
|
|
617
|
+
if (this.stopped || this.rebuildTimer !== undefined) return;
|
|
618
|
+
this.rebuildTimer = setTimeout(() => {
|
|
619
|
+
this.rebuildTimer = undefined;
|
|
620
|
+
void this.rebuildNow().catch((error: unknown) => {
|
|
621
|
+
// A background rebuild failure is a soft miss: requests served the
|
|
622
|
+
// last-completed snapshot, and the next invalidation retries.
|
|
623
|
+
log.warn("[route-snapshot] background rebuild failed", error as Error);
|
|
624
|
+
});
|
|
625
|
+
}, this.rebuildDebounceMs);
|
|
626
|
+
if (typeof this.rebuildTimer.unref === "function") this.rebuildTimer.unref();
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
/** Starts (or joins) the build for the current revision. */
|
|
630
|
+
private rebuildNow(): Promise<RouteSnapshot> {
|
|
555
631
|
const revision = this.revision;
|
|
632
|
+
if (this.building && this.building.revision === revision) return this.building.promise;
|
|
556
633
|
const promise = this.build(revision);
|
|
557
634
|
this.building = { revision, promise };
|
|
558
|
-
|
|
559
|
-
return await promise;
|
|
560
|
-
} finally {
|
|
635
|
+
void promise.finally(() => {
|
|
561
636
|
// Only the build that is still current may clear the slot; a build
|
|
562
637
|
// started after a mutation must not be dropped by an older one finishing.
|
|
563
638
|
if (this.building?.promise === promise) this.building = undefined;
|
|
564
|
-
}
|
|
639
|
+
});
|
|
640
|
+
return promise;
|
|
565
641
|
}
|
|
566
642
|
|
|
643
|
+
async getSnapshot(): Promise<RouteSnapshot> {
|
|
644
|
+
// G-02: serve the last-completed snapshot while a rebuild runs, so the
|
|
645
|
+
// page-open request path never pays the catalog build. Only a cold start
|
|
646
|
+
// (no snapshot at all) falls through to a synchronous build here.
|
|
647
|
+
if (this.snapshot) return this.snapshot;
|
|
648
|
+
return this.rebuildNow();
|
|
649
|
+
}
|
|
567
650
|
|
|
568
651
|
async invalidate(): Promise<RoutingRevision> {
|
|
569
652
|
this.revision += 1;
|
|
570
|
-
|
|
571
|
-
//
|
|
572
|
-
//
|
|
573
|
-
//
|
|
574
|
-
//
|
|
653
|
+
// G-02: trigger a BACKGROUND rebuild immediately instead of clearing the
|
|
654
|
+
// snapshot and making the next request pay. The rebuild is debounced so a
|
|
655
|
+
// burst of invalidates (a recovery sweep, a cascade of console writes)
|
|
656
|
+
// coalesces into one rebuild, and it never blocks the mutating request —
|
|
657
|
+
// a console write does not wait on its own rebuild here.
|
|
658
|
+
this.scheduleRebuild();
|
|
575
659
|
// Broadcast, because `reusePort` runs several gateway processes and each
|
|
576
660
|
// holds its own snapshot: a console write handled by one of them left the
|
|
577
661
|
// others serving pre-write routing until something else invalidated them.
|
|
@@ -581,6 +665,15 @@ export class InMemoryRouteSnapshotService implements RouteSnapshotService {
|
|
|
581
665
|
return this.revision;
|
|
582
666
|
}
|
|
583
667
|
|
|
668
|
+
/** Stops background rebuilds. Test/teardown helper. */
|
|
669
|
+
stop(): void {
|
|
670
|
+
this.stopped = true;
|
|
671
|
+
if (this.rebuildTimer !== undefined) {
|
|
672
|
+
clearTimeout(this.rebuildTimer);
|
|
673
|
+
this.rebuildTimer = undefined;
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
|
|
584
677
|
private async build(revision: RoutingRevision): Promise<RouteSnapshot> {
|
|
585
678
|
const built = await this.builder();
|
|
586
679
|
const providerRouting: ProviderRoutingMap | undefined = built.providerRouting
|