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
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vision-handoff sub-dispatch: describes the images of a canonical request by
|
|
3
|
+
* calling a configured vision model, then returns the description text so the
|
|
4
|
+
* caller can fold it back into the original request as text.
|
|
5
|
+
*
|
|
6
|
+
* The sub-request is a plain non-streaming chat request built from only the
|
|
7
|
+
* user turns that carried images (no history, no tools, no system prompt) and
|
|
8
|
+
* is dispatched through the shared attempt loop — admission, pool slot,
|
|
9
|
+
* retry, cooldown, health, and accounting all ride the same policy as any
|
|
10
|
+
* other route. Each model on the configured list is tried in order; the first
|
|
11
|
+
* that produces non-empty text wins. This is what makes the fallback "if
|
|
12
|
+
* vision model 1 is unhealthy, drop to the next one" work without a new
|
|
13
|
+
* failover engine.
|
|
14
|
+
*/
|
|
15
|
+
import { createHash } from "node:crypto";
|
|
16
|
+
import { GatewayError } from "../protocol/gateway-error";
|
|
17
|
+
import { log } from "../observability/logger";
|
|
18
|
+
import type { CanonicalEvent, CanonicalMessage, CanonicalRequest, ContentPart } from "../protocol/canonical-model";
|
|
19
|
+
import type { ResolvedApiKey } from "../security/api-key-auth";
|
|
20
|
+
import type { ProviderAdapter } from "../providers/provider-contracts";
|
|
21
|
+
import { resolveCredentialForAccount } from "../providers/operations/provider-credential-service";
|
|
22
|
+
import { runAttemptLoop } from "./dispatch/attempt-loop";
|
|
23
|
+
import { completeAttempt, completionContext, estimatedUsage, terminalFailure } from "./dispatch/attempt-finalize";
|
|
24
|
+
import { repriceUsage } from "../providers/provider-usage";
|
|
25
|
+
import { projectForRoute, routeCapabilitiesFor } from "../protocol/translation/capabilities";
|
|
26
|
+
import { buildUpstreamDispatchContext } from "./dispatch/upstream";
|
|
27
|
+
import type { GatewayHandlerDeps } from "./dispatch/handler-deps";
|
|
28
|
+
import type { ByokUpstreamHost } from "../providers/operations/provider-catalog-service";
|
|
29
|
+
import type { PreparedProxyRequest } from "./request/preparer";
|
|
30
|
+
import { getCachedVisionDescription, hashImagePart, setCachedVisionDescription } from "./vision-handoff-cache";
|
|
31
|
+
|
|
32
|
+
/** Dependency surface needed to run a vision handoff (a gateway-handler deps). */
|
|
33
|
+
export interface VisionHandoffDeps extends GatewayHandlerDeps {
|
|
34
|
+
/** Live lookup of a provider's SSRF-validated upstream host. */
|
|
35
|
+
readonly byokUpstreamHosts?: { readonly get: (providerId: string) => ByokUpstreamHost | undefined };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Instruction prepended to the first image-carrying user turn. */
|
|
39
|
+
const VISION_HANDOFF_INSTRUCTION =
|
|
40
|
+
"Describe the contents of the image(s) attached to this message. Be factual, " +
|
|
41
|
+
"concise, and specific: mention visible text, people, objects, layout, colors, " +
|
|
42
|
+
"and anything else notable.";
|
|
43
|
+
|
|
44
|
+
/** Minimum remaining deadline before we attempt a handoff at all. */
|
|
45
|
+
const VISION_HANDOFF_MIN_BUDGET_MS = 2_000;
|
|
46
|
+
|
|
47
|
+
/** Per-model budget cap so one slow vision model cannot eat the whole deadline. */
|
|
48
|
+
const VISION_HANDOFF_PER_MODEL_TIMEOUT_MS = 12_000;
|
|
49
|
+
|
|
50
|
+
/** The result of one successful handoff. */
|
|
51
|
+
export interface VisionHandoffResult {
|
|
52
|
+
readonly description: string;
|
|
53
|
+
readonly model: string;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Inputs for running the handoff across an ordered model list. */
|
|
57
|
+
|
|
58
|
+
/** The shape the preparer calls; matches `runVisionHandoff` in preparer deps. */
|
|
59
|
+
export interface VisionHandoffCall {
|
|
60
|
+
readonly canonicalRequest: CanonicalRequest;
|
|
61
|
+
readonly authorization: ResolvedApiKey;
|
|
62
|
+
readonly deadlineMs: number;
|
|
63
|
+
readonly signal?: AbortSignal;
|
|
64
|
+
readonly clientUserAgent?: string;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* True when a content part is an image or the legacy `[image]` placeholder
|
|
69
|
+
* that a degraded (non-image-capable) route substitutes for one.
|
|
70
|
+
*/
|
|
71
|
+
export function isImageOrPlaceholderPart(part: ContentPart): boolean {
|
|
72
|
+
return part.kind === "image" || (part.kind === "text" && part.text === "[image]");
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Index of the last user turn that carries images (or their `[image]`
|
|
77
|
+
* placeholders). The handoff describes only this turn; older image-carrying
|
|
78
|
+
* turns are history and are dropped by the caller's replacement pass (they
|
|
79
|
+
* are not sent to the vision model either).
|
|
80
|
+
*/
|
|
81
|
+
export function lastImageCarryingUserTurnIndex(request: CanonicalRequest): number {
|
|
82
|
+
for (let i = request.messages.length - 1; i >= 0; i -= 1) {
|
|
83
|
+
const message = request.messages[i]!;
|
|
84
|
+
if (message.role === "user" && message.content.some(isImageOrPlaceholderPart)) {
|
|
85
|
+
return i;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return -1;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** Index of the last user turn that carries a real image (not a placeholder). */
|
|
92
|
+
function lastRealImageCarryingUserTurnIndex(request: CanonicalRequest): number {
|
|
93
|
+
for (let i = request.messages.length - 1; i >= 0; i -= 1) {
|
|
94
|
+
const message = request.messages[i]!;
|
|
95
|
+
if (message.role === "user" && message.content.some((part) => part.kind === "image")) {
|
|
96
|
+
return i;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return -1;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** Hash of the active image set (the last image-carrying user turn's images). */
|
|
103
|
+
function hashActiveImageSet(request: CanonicalRequest): string {
|
|
104
|
+
const index = lastRealImageCarryingUserTurnIndex(request);
|
|
105
|
+
if (index < 0) return "";
|
|
106
|
+
const images = request.messages[index]!.content.filter(
|
|
107
|
+
(part): part is { kind: "image"; payload: unknown } => part.kind === "image",
|
|
108
|
+
);
|
|
109
|
+
const hashes = images.map(hashImagePart).sort();
|
|
110
|
+
return createHash("sha256").update(hashes.join("|")).digest("hex");
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Builds the sub-request sent to one vision model: only the *last*
|
|
115
|
+
* image-carrying user turn, keeping its sibling text parts, prefixed by a
|
|
116
|
+
* short describe-the-image instruction. No history, no tools, no system
|
|
117
|
+
* prompt. The sub-request streams internally (`stream = true`) so the parent
|
|
118
|
+
* keeps its own streaming reply once the description is folded in; the
|
|
119
|
+
* description itself must still be whole before the parent is dispatched.
|
|
120
|
+
*/
|
|
121
|
+
function buildVisionSubRequest(parent: CanonicalRequest, model: string): CanonicalRequest {
|
|
122
|
+
const index = lastRealImageCarryingUserTurnIndex(parent);
|
|
123
|
+
if (index < 0) {
|
|
124
|
+
throw new GatewayError(
|
|
125
|
+
"invalid_request",
|
|
126
|
+
400,
|
|
127
|
+
"vision handoff cannot build a sub-request from a request with no image-carrying user turns (request {requestId})",
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
const message = parent.messages[index]!;
|
|
131
|
+
const kept = message.content.filter((part) => part.kind === "text" || part.kind === "image");
|
|
132
|
+
const messages: CanonicalMessage[] = [
|
|
133
|
+
{ role: "user", content: [{ kind: "text", text: VISION_HANDOFF_INSTRUCTION }, ...kept] },
|
|
134
|
+
];
|
|
135
|
+
return {
|
|
136
|
+
model,
|
|
137
|
+
messages,
|
|
138
|
+
generation_controls: {},
|
|
139
|
+
stream: true,
|
|
140
|
+
source_surface: "chat",
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Tries the ordered vision-model list and returns every description produced
|
|
146
|
+
* (one per model that answered). Each model gets its own attempt-loop
|
|
147
|
+
* sub-dispatch with its own admission/pool slot (isolated from the parent
|
|
148
|
+
* request) and its own child request state. Any failure — an unroutable or
|
|
149
|
+
* unauthorized model, an upstream error, an empty reply — falls through to
|
|
150
|
+
* the next entry.
|
|
151
|
+
*/
|
|
152
|
+
export async function tryVisionHandoff(
|
|
153
|
+
deps: VisionHandoffDeps,
|
|
154
|
+
input: VisionHandoffCall,
|
|
155
|
+
models: readonly string[],
|
|
156
|
+
): Promise<readonly VisionHandoffResult[]> {
|
|
157
|
+
// Reusing the exact same active image set (same image, same conversation
|
|
158
|
+
// replay) skips the vision call entirely.
|
|
159
|
+
const cacheHash = hashActiveImageSet(input.canonicalRequest);
|
|
160
|
+
const cached = cacheHash.length > 0 ? getCachedVisionDescription(cacheHash) : undefined;
|
|
161
|
+
if (cached !== undefined && cached.trim().length > 0) {
|
|
162
|
+
return [{ description: cached.trim(), model: models[0] ?? "cache" }];
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const results: readonly VisionHandoffResult[] = await trialModelsInParallel(deps, input, models);
|
|
166
|
+
if (cacheHash.length > 0 && results.length > 0) {
|
|
167
|
+
setCachedVisionDescription(cacheHash, results[0]!.description);
|
|
168
|
+
}
|
|
169
|
+
return results;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** Tries the ordered vision-model list, in parallel, with a per-model budget. */
|
|
173
|
+
async function trialModelsInParallel(
|
|
174
|
+
deps: VisionHandoffDeps,
|
|
175
|
+
input: VisionHandoffCall,
|
|
176
|
+
models: readonly string[],
|
|
177
|
+
): Promise<readonly VisionHandoffResult[]> {
|
|
178
|
+
if (models.length === 0) return [];
|
|
179
|
+
const remaining = input.deadlineMs - Date.now();
|
|
180
|
+
if (remaining <= VISION_HANDOFF_MIN_BUDGET_MS) return [];
|
|
181
|
+
|
|
182
|
+
const perModelBudget = Math.min(
|
|
183
|
+
remaining,
|
|
184
|
+
VISION_HANDOFF_PER_MODEL_TIMEOUT_MS,
|
|
185
|
+
);
|
|
186
|
+
const decided = await Promise.all(
|
|
187
|
+
models.map((model) => trialOneModel(deps, input, model, perModelBudget)),
|
|
188
|
+
);
|
|
189
|
+
return decided.filter(
|
|
190
|
+
(result): result is VisionHandoffResult => result !== null,
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/** Runs one vision-model trial with its own attempt-loop. Null on any failure. */
|
|
195
|
+
async function trialOneModel(
|
|
196
|
+
deps: VisionHandoffDeps,
|
|
197
|
+
input: VisionHandoffCall,
|
|
198
|
+
model: string,
|
|
199
|
+
budgetMs: number,
|
|
200
|
+
): Promise<VisionHandoffResult | null> {
|
|
201
|
+
const childRequest = new Request("http://cartethyia.invalid/vision-handoff", {
|
|
202
|
+
method: "POST",
|
|
203
|
+
});
|
|
204
|
+
const childState = deps.stateStore.initialize(childRequest, Date.now(), budgetMs);
|
|
205
|
+
childState.authorization = input.authorization;
|
|
206
|
+
childState.ingressBody = null;
|
|
207
|
+
try {
|
|
208
|
+
let prepared: PreparedProxyRequest;
|
|
209
|
+
try {
|
|
210
|
+
prepared = await deps.proxyPreparer.prepare({
|
|
211
|
+
canonicalRequest: buildVisionSubRequest(input.canonicalRequest, model),
|
|
212
|
+
authorization: input.authorization,
|
|
213
|
+
deadlineMs: budgetMs,
|
|
214
|
+
// The sub-request itself must never trigger a nested handoff.
|
|
215
|
+
disableVisionHandoff: true,
|
|
216
|
+
...(input.signal === undefined ? {} : { signal: input.signal }),
|
|
217
|
+
...(input.clientUserAgent === undefined ? {} : { clientUserAgent: input.clientUserAgent }),
|
|
218
|
+
});
|
|
219
|
+
} catch (error) {
|
|
220
|
+
// Unroutable / not allowed for this key / no account — try the next.
|
|
221
|
+
log.warn("[vision-handoff] vision model skipped", {
|
|
222
|
+
model,
|
|
223
|
+
tenantId: input.authorization.tenantId,
|
|
224
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
225
|
+
});
|
|
226
|
+
return null;
|
|
227
|
+
}
|
|
228
|
+
if (prepared.candidate.capability_profile.image !== true) return null;
|
|
229
|
+
childState.canonicalRequest = prepared.canonicalRequest;
|
|
230
|
+
childState.preparedRequest = prepared;
|
|
231
|
+
|
|
232
|
+
const text = await runAttemptLoop<string, ProviderAdapter>({
|
|
233
|
+
state: childState,
|
|
234
|
+
deps,
|
|
235
|
+
leaseSource: {
|
|
236
|
+
admissionService: prepared.admissionService,
|
|
237
|
+
routingEngine: prepared.routingEngine,
|
|
238
|
+
plan: prepared.plan,
|
|
239
|
+
estimatedInputTokens: prepared.estimatedInputTokens,
|
|
240
|
+
estimatedOutputTokens: prepared.estimatedOutputTokens,
|
|
241
|
+
authorizationSnapshot: prepared.authorization.snapshot,
|
|
242
|
+
},
|
|
243
|
+
candidates:
|
|
244
|
+
prepared.eligibleRouteCandidates.length > 0
|
|
245
|
+
? prepared.eligibleRouteCandidates
|
|
246
|
+
: [prepared.candidate],
|
|
247
|
+
tenantId: prepared.authorization.tenantId,
|
|
248
|
+
strictPoolSelection: true,
|
|
249
|
+
resolveHost: (candidate) => deps.byokUpstreamHosts?.get(candidate.provider_id),
|
|
250
|
+
exhaustedError: new GatewayError(
|
|
251
|
+
"route_unavailable",
|
|
252
|
+
503,
|
|
253
|
+
"no eligible vision route (request {requestId}). The bouncer stepped out — come back shortly 🚪",
|
|
254
|
+
),
|
|
255
|
+
prepare: async (candidate) => {
|
|
256
|
+
if (!candidate.provider_account_id && candidate.requires_account !== false) {
|
|
257
|
+
throw new GatewayError(
|
|
258
|
+
"route_unavailable",
|
|
259
|
+
503,
|
|
260
|
+
"This route has no usable credential (request {requestId}). Save one on the account, or route to a provider whose account is configured. Wrong key, right door — check the credential 🔑",
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
const credential = candidate.provider_account_id
|
|
264
|
+
? await resolveCredentialForAccount(
|
|
265
|
+
deps.db,
|
|
266
|
+
candidate.provider_id,
|
|
267
|
+
candidate.provider_account_id,
|
|
268
|
+
deps.oauthRefreshService && deps.resolveOAuthRefresher
|
|
269
|
+
? { refreshService: deps.oauthRefreshService, resolveRefresher: deps.resolveOAuthRefresher }
|
|
270
|
+
: undefined,
|
|
271
|
+
)
|
|
272
|
+
: {
|
|
273
|
+
provider_id: candidate.provider_id as ProviderAdapter["provider_id"],
|
|
274
|
+
credential_kind: "none" as const,
|
|
275
|
+
};
|
|
276
|
+
const adapter = await deps.resolveProviderAdapter?.(candidate.provider_id);
|
|
277
|
+
if (!adapter) {
|
|
278
|
+
throw new GatewayError(
|
|
279
|
+
"route_unavailable",
|
|
280
|
+
503,
|
|
281
|
+
"provider adapter not registered (request {requestId}). Even we have bad days — check the registry wiring 🐻",
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
return { credential, adapter };
|
|
285
|
+
},
|
|
286
|
+
attempt: async (context) => {
|
|
287
|
+
const { candidate, credential, adapter, leases } = context;
|
|
288
|
+
const candidateRequest =
|
|
289
|
+
candidate.model_id === prepared.canonicalRequest.model
|
|
290
|
+
? prepared.canonicalRequest
|
|
291
|
+
: { ...prepared.canonicalRequest, model: candidate.model_id };
|
|
292
|
+
const attemptRequest = projectForRoute(candidateRequest, routeCapabilitiesFor(candidate));
|
|
293
|
+
const providerRouteCandidate = {
|
|
294
|
+
provider_id: candidate.provider_id,
|
|
295
|
+
model_id: candidate.model_id,
|
|
296
|
+
wire_family: candidate.wire_family,
|
|
297
|
+
endpoint_path: candidate.endpoint,
|
|
298
|
+
...(candidate.output_limit == null ? {} : { output_limit: candidate.output_limit }),
|
|
299
|
+
capabilities: candidate.capability_profile,
|
|
300
|
+
user_agent: candidate.user_agent,
|
|
301
|
+
...(candidate.dialect === undefined ? {} : { dialect: candidate.dialect }),
|
|
302
|
+
};
|
|
303
|
+
const dispatchContextBase = buildUpstreamDispatchContext({
|
|
304
|
+
credential,
|
|
305
|
+
deadline: childState.deadlineMs,
|
|
306
|
+
signal: childState.abortController.signal,
|
|
307
|
+
headers: {},
|
|
308
|
+
requestId: childState.requestId,
|
|
309
|
+
...(candidate.user_agent === undefined ? {} : { userAgent: candidate.user_agent }),
|
|
310
|
+
...(deps.networkBindingFactory
|
|
311
|
+
? {
|
|
312
|
+
outboundFetch: deps.networkBindingFactory.fetch(
|
|
313
|
+
leases.proxySlot?.poolId ?? "",
|
|
314
|
+
prepared.authorization.tenantId,
|
|
315
|
+
),
|
|
316
|
+
}
|
|
317
|
+
: {}),
|
|
318
|
+
});
|
|
319
|
+
const events: CanonicalEvent[] = [];
|
|
320
|
+
for await (const event of adapter.dispatch(
|
|
321
|
+
attemptRequest,
|
|
322
|
+
providerRouteCandidate as never,
|
|
323
|
+
dispatchContextBase as never,
|
|
324
|
+
)) {
|
|
325
|
+
events.push(event);
|
|
326
|
+
}
|
|
327
|
+
const terminal = events.find((event) => event.type === "terminal");
|
|
328
|
+
if (terminal === undefined) throw terminalFailure(undefined);
|
|
329
|
+
const terminalError = terminalFailure(terminal);
|
|
330
|
+
if (terminalError !== undefined) throw terminalError;
|
|
331
|
+
let text = "";
|
|
332
|
+
for (const event of events) {
|
|
333
|
+
if (event.type === "content_delta" && event.content.kind === "text") {
|
|
334
|
+
text += event.content.text;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
const usage =
|
|
338
|
+
terminal.usage ??
|
|
339
|
+
estimatedUsage(prepared.estimatedInputTokens, prepared.estimatedOutputTokens);
|
|
340
|
+
const pricedUsage = repriceUsage(usage, candidate.provider_id, candidate.model_id);
|
|
341
|
+
await completeAttempt(childState, {
|
|
342
|
+
status: "completed",
|
|
343
|
+
...completionContext({
|
|
344
|
+
providerId: candidate.provider_id,
|
|
345
|
+
modelId: candidate.model_id,
|
|
346
|
+
tenantId: prepared.authorization.tenantId,
|
|
347
|
+
ingressBody: null,
|
|
348
|
+
db: deps.db,
|
|
349
|
+
...(candidate.provider_account_id ? { accountId: candidate.provider_account_id } : {}),
|
|
350
|
+
...(candidate.provider_account_label ? { accountLabel: candidate.provider_account_label } : {}),
|
|
351
|
+
...(leases.proxySlot ? { networkPoolId: leases.proxySlot.poolId } : {}),
|
|
352
|
+
...(leases.lease ? { lease: leases.lease } : {}),
|
|
353
|
+
...(deps.telemetryBuffer ? { telemetryBuffer: deps.telemetryBuffer } : {}),
|
|
354
|
+
...(deps.snapshotService ? { snapshotService: deps.snapshotService } : {}),
|
|
355
|
+
}),
|
|
356
|
+
usage: pricedUsage,
|
|
357
|
+
commitUsage: pricedUsage,
|
|
358
|
+
responseBody: null,
|
|
359
|
+
clientResponseText: text,
|
|
360
|
+
});
|
|
361
|
+
return text;
|
|
362
|
+
},
|
|
363
|
+
});
|
|
364
|
+
if (text.trim().length === 0) return null;
|
|
365
|
+
return { description: text.trim(), model };
|
|
366
|
+
} catch (error) {
|
|
367
|
+
log.warn("[vision-handoff] vision model attempt failed", {
|
|
368
|
+
model,
|
|
369
|
+
tenantId: input.authorization.tenantId,
|
|
370
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
371
|
+
});
|
|
372
|
+
return null;
|
|
373
|
+
} finally {
|
|
374
|
+
childState.cleanup();
|
|
375
|
+
}
|
|
376
|
+
}
|
|
@@ -17,7 +17,7 @@ import { decodeJwtPayload } from "../providers/authentication/oauth-flow-store";
|
|
|
17
17
|
import { runSweep } from "./sweep-runner";
|
|
18
18
|
|
|
19
19
|
/** One account whose stored credential has never had its identity read. */
|
|
20
|
-
|
|
20
|
+
interface UnenrichedAccount {
|
|
21
21
|
readonly id: string;
|
|
22
22
|
readonly providerId: string;
|
|
23
23
|
readonly label: string;
|
|
@@ -29,7 +29,7 @@ export interface UnenrichedAccount {
|
|
|
29
29
|
readonly credentialCiphertext: Buffer | Uint8Array | null;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
interface AccountIdentityEnrichmentSweepDeps {
|
|
33
33
|
readonly db: CartethyiaDatabase;
|
|
34
34
|
/**
|
|
35
35
|
* How many accounts to enrich in one pass. Identity parsing is pure CPU on
|