cartethyia 2.7.3 → 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-2TJD37Hb.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-xefJNRmJ.js → CliToolDetailPage-DZKmVuJN.js} +1 -1
- package/dist/dashboard/assets/CliToolDetailPage-DZKmVuJN.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolsPage-Rj9iHQ3F.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-BKj_r0rp.js +1 -0
- 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-C4w7vea-.js → LoginPage-DkXb6dx-.js} +1 -1
- package/dist/dashboard/assets/LoginPage-DkXb6dx-.js.gz +0 -0
- package/dist/dashboard/assets/{ModelPicker-BtPcAfot.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-5SnUSy3j.js +9 -0
- 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-BQS0Rx_u.js → SetupPage-WTa0sktC.js} +1 -1
- package/dist/dashboard/assets/SetupPage-WTa0sktC.js.gz +0 -0
- package/dist/dashboard/assets/{StudioPage-TP7voAQ0.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-C0YYuZJ4.js → assert-C0K-c4eo.js} +1 -1
- package/dist/dashboard/assets/assert-C0K-c4eo.js.gz +0 -0
- package/dist/dashboard/assets/{badge-B_DMpMJN.js → badge-DfbhQU4k.js} +1 -1
- package/dist/dashboard/assets/badge-DfbhQU4k.js.gz +0 -0
- package/dist/dashboard/assets/{banner-DMrMPmth.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-Be5Uzamx.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-muFRphDu.js → cli-tools-CQXVMn4P.js} +1 -1
- package/dist/dashboard/assets/cli-tools-CQXVMn4P.js.gz +0 -0
- package/dist/dashboard/assets/{clipboard-button-DOfrVmsi.js → clipboard-button-BQqBQd2n.js} +1 -1
- package/dist/dashboard/assets/clipboard-button-BQqBQd2n.js.gz +0 -0
- package/dist/dashboard/assets/{dialog-C9omdjQt.js → dialog-BEK7KP0H.js} +1 -1
- package/dist/dashboard/assets/dialog-BEK7KP0H.js.gz +0 -0
- package/dist/dashboard/assets/{drawer-Bm8WKaGY.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-q5HwZ8nN.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-BTScrQpe.js → formatters-BAACA727.js} +1 -1
- package/dist/dashboard/assets/formatters-BAACA727.js.gz +0 -0
- package/dist/dashboard/assets/{github-badge-Orjuzemr.js → github-badge-YSCE4UC2.js} +1 -1
- package/dist/dashboard/assets/github-badge-YSCE4UC2.js.gz +0 -0
- package/dist/dashboard/assets/{index-cPrX5vn6.js → index-FQlj0HUT.js} +2 -2
- package/dist/dashboard/assets/index-FQlj0HUT.js.gz +0 -0
- package/dist/dashboard/assets/{inline-Dvux8w-0.js → inline-C4gU5iOL.js} +1 -1
- package/dist/dashboard/assets/inline-C4gU5iOL.js.gz +0 -0
- package/dist/dashboard/assets/{input-CU1Rgf3p.js → input-BHHX_Tmm.js} +1 -1
- package/dist/dashboard/assets/input-BHHX_Tmm.js.gz +0 -0
- package/dist/dashboard/assets/{layout-C_7A6H55.js → layout-CajrYUnD.js} +1 -1
- package/dist/dashboard/assets/{layout-C_7A6H55.js.gz → layout-CajrYUnD.js.gz} +0 -0
- package/dist/dashboard/assets/{list-row-CzFvogbj.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/minimize-2-BTmMTK4o.js +1 -0
- package/dist/dashboard/assets/minimize-2-BTmMTK4o.js.gz +0 -0
- package/dist/dashboard/assets/{mutation-JJGQm8TD.js → mutation-Rr0TIvcj.js} +1 -1
- package/dist/dashboard/assets/mutation-Rr0TIvcj.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/{query-keys-CLaNz8Xz.js → query-keys-BloKtaXJ.js} +1 -1
- package/dist/dashboard/assets/query-keys-BloKtaXJ.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-BEH32QOC.js → routing-in0f_IfK.js} +1 -1
- package/dist/dashboard/assets/routing-in0f_IfK.js.gz +0 -0
- package/dist/dashboard/assets/{select-B3iTetHN.js → select-BLULNqTp.js} +2 -2
- 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-KrOrm7JZ.js → stack-D2lIR0tg.js} +1 -1
- package/dist/dashboard/assets/stack-D2lIR0tg.js.gz +0 -0
- package/dist/dashboard/assets/{state-DLS7IDMy.js → state-DTARjfiW.js} +1 -1
- package/dist/dashboard/assets/state-DTARjfiW.js.gz +0 -0
- package/dist/dashboard/assets/{switch-42ItvbAs.js → switch-c0oPSnqW.js} +1 -1
- package/dist/dashboard/assets/switch-c0oPSnqW.js.gz +0 -0
- package/dist/dashboard/assets/{useMutation-Dmnfh1Ue.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 +59 -57
- package/migrations/full-postgres/0027_qoder_identity_genuine_seed.sql +9 -10
- package/migrations/full-postgres/0032_add_provider_pool_accounts.sql +53 -0
- package/migrations/full-postgres/0033_add_api_key_ban.sql +9 -0
- package/migrations/full-postgres/0034_add_api_key_ban_until.sql +7 -0
- package/migrations/full-postgres/0035_align_pool_account_status_enum.sql +21 -0
- 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/0027_qoder_identity_genuine_seed.sql +9 -10
- package/migrations/lite-sqlite3/0032_add_provider_pool_accounts.sql +44 -0
- package/migrations/lite-sqlite3/0033_add_api_key_ban.sql +8 -0
- package/migrations/lite-sqlite3/0034_add_api_key_ban_until.sql +5 -0
- package/migrations/lite-sqlite3/0035_align_pool_account_status_enum.sql +6 -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 -5
- package/scripts/cli/update.ts +15 -41
- package/src/boot/app.ts +67 -10
- 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 +8 -4
- 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 +92 -31
- package/src/console/api-keys/routes.ts +131 -6
- package/src/console/audit/routes.ts +3 -3
- package/src/console/auth/access.ts +4 -4
- package/src/console/auth/account-reset.ts +5 -5
- 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 +40 -34
- 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 +11 -9
- package/src/console/backup/export-endpoint.ts +11 -12
- package/src/console/backup/nine-router.ts +1 -7
- package/src/console/backup/routes.ts +32 -36
- 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 +4 -4
- package/src/console/cli-tools/service.ts +1 -1
- package/src/console/cli-tools/store.ts +2 -2
- package/src/console/console-router-guards.ts +2 -2
- package/src/console/console-router.ts +36 -4
- package/src/console/dashboard-assets.ts +9 -9
- package/src/console/data-management/routes.ts +3 -3
- package/src/console/domain-registration.ts +18 -4
- 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 +5 -1
- package/src/console/observability/live.ts +34 -21
- package/src/console/observability/logs.ts +4 -4
- package/src/console/observability/performance.ts +3 -3
- package/src/console/observability/routes.ts +3 -3
- package/src/console/observability/sse.ts +62 -8
- package/src/console/observability/store.ts +36 -20
- package/src/console/providers/catalog/catalog-projections.ts +1 -1
- package/src/console/providers/catalog/contracts.ts +82 -22
- package/src/console/providers/catalog/model-operations.ts +1 -1
- package/src/console/providers/catalog/provider-operations.ts +110 -17
- package/src/console/providers/catalog/public-model-store.ts +0 -0
- package/src/console/providers/catalog/routes.ts +30 -6
- package/src/console/providers/catalog/store-helpers.ts +349 -326
- package/src/console/providers/catalog/store.ts +1662 -1292
- package/src/console/providers/detail/routes.ts +4 -4
- package/src/console/providers/oauth/callback-listener.ts +100 -18
- package/src/console/providers/oauth/routes.ts +95 -16
- 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 +285 -67
- package/src/console/quota/account-test-egress.ts +4 -4
- 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/model/routes.ts +3 -3
- package/src/console/routing/pools/contracts.ts +13 -10
- package/src/console/routing/pools/import-parse.ts +3 -3
- package/src/console/routing/pools/operations.ts +14 -7
- package/src/console/routing/pools/probe-verdict.ts +8 -3
- package/src/console/routing/pools/relay-deploy.ts +12 -12
- package/src/console/routing/pools/routes.ts +3 -3
- package/src/console/routing/pools/store.ts +21 -1
- package/src/console/settings/contracts.ts +29 -2
- package/src/console/settings/routes.ts +3 -3
- package/src/console/settings/store.ts +20 -0
- package/src/console/share/share-router.ts +112 -44
- package/src/console/share/share-stats.ts +109 -39
- 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 +42 -6
- 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 +256 -98
- 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 +76 -5
- package/src/network/pool/agent.ts +7 -6
- package/src/network/pool/byte-accounting.ts +1 -1
- package/src/network/pool/resolver.ts +55 -51
- package/src/network/pool/selector.ts +150 -136
- package/src/network/pool-cooldown-store.ts +71 -16
- package/src/network/pool-health-machine.ts +22 -2
- package/src/network/rate-limit-verdict.ts +68 -0
- package/src/network/retry.ts +1 -1
- package/src/network/ssrf.ts +24 -75
- package/src/observability/event-log.ts +2 -2
- package/src/observability/event-query.ts +286 -214
- 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/telemetry-status.ts +1 -1
- 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 +10 -5
- package/src/persistence/dialect.ts +5 -1
- package/src/persistence/readiness.ts +19 -7
- package/src/persistence/redis.ts +1 -1
- package/src/persistence/schema.ts +177 -45
- package/src/persistence/shared-cache.ts +2 -2
- package/src/persistence/sort-order.ts +1 -1
- package/src/persistence/stores/api-key-store.ts +109 -4
- package/src/persistence/stores/share-store.ts +20 -8
- package/src/persistence/stores/telemetry-store.ts +1 -1
- 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 +12 -16
- package/src/protocol/dsml.ts +3 -49
- package/src/protocol/gateway-error.ts +6 -3
- package/src/protocol/image-clamp.ts +2 -2
- package/src/protocol/image-generation.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 +27 -12
- package/src/protocol/response/codex.ts +2 -2
- package/src/protocol/response/gemini.ts +1 -1
- package/src/protocol/response/messages.ts +6 -6
- package/src/protocol/response/omp-tool-call-markup.ts +1 -1
- package/src/protocol/response/responses.ts +3 -3
- package/src/protocol/sse-decoder.ts +64 -4
- 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 +8 -8
- 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/protocol/upstream/send-claude-messages.ts +3 -3
- package/src/providers/adapters/base-adapter.ts +1 -1
- package/src/providers/adapters/compatible-adapter.ts +44 -5
- 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/authentication/oauth-refresh-service.ts +2 -2
- package/src/providers/bundled-discovery-providers.ts +6 -30
- package/src/providers/configured-openai-providers.ts +82 -97
- package/src/providers/configured-provider.ts +1 -1
- 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 +50 -15
- package/src/providers/discovery/probing-service.ts +18 -20
- package/src/providers/integrations/agentrouter/agentrouter.ts +14 -5
- package/src/providers/integrations/alibabacp/alibabacp.ts +2 -2
- package/src/providers/integrations/anthropic.ts +4 -11
- 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 +15 -26
- package/src/providers/integrations/buddy/buddy-catalog-shared.ts +2 -2
- 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-discovery-shared.ts +37 -6
- package/src/providers/integrations/buddy/buddy-identity.ts +108 -1
- package/src/providers/integrations/buddy/buddy-media.ts +16 -16
- 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 +5 -16
- package/src/providers/integrations/buddy/codebuddy/shared.ts +30 -9
- package/src/providers/integrations/buddy/workbuddy/provider.ts +6 -10
- package/src/providers/integrations/buddy/workbuddy/shared.ts +21 -3
- package/src/providers/integrations/claude/claude-betas.ts +1 -1
- package/src/providers/integrations/claude/claude-cch.ts +148 -148
- package/src/providers/integrations/claude/claude-credentials.ts +1 -1
- package/src/providers/integrations/claude/claude-slow-lane-retry.ts +1 -1
- package/src/providers/integrations/claude/claude.ts +3 -10
- package/src/providers/integrations/claude/fallback-credit.ts +3 -7
- package/src/providers/integrations/cline/cline.ts +2 -2
- package/src/providers/integrations/cloudflareai/cloudflareai.ts +4 -7
- package/src/providers/integrations/codex/codex-device-code.ts +101 -101
- package/src/providers/integrations/codex/codex-errors.ts +1 -1
- package/src/providers/integrations/codex/codex-oauth.ts +0 -1
- package/src/providers/integrations/codex/codex.ts +14 -15
- package/src/providers/integrations/commandcode/commandcode.ts +76 -116
- package/src/providers/integrations/commandcode/quota.ts +132 -48
- package/src/providers/integrations/cursor/cursor-http2.ts +11 -14
- 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 +53 -20
- 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 +13 -13
- package/src/providers/integrations/gemini.ts +11 -11
- 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 +47 -20
- 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 +88 -42
- package/src/providers/integrations/inferhub.ts +4 -11
- package/src/providers/integrations/kilo/kilo-oauth.ts +1 -1
- package/src/providers/integrations/kimi/kimi.ts +4 -11
- 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 +91 -78
- package/src/providers/integrations/muse/muse-oauth.ts +0 -1
- package/src/providers/integrations/muse/muse-quota.ts +0 -4
- package/src/providers/integrations/muse/muse.ts +6 -9
- package/src/providers/integrations/opencode/fingerprint.ts +7 -5
- package/src/providers/integrations/opencode/provider.ts +17 -24
- package/src/providers/integrations/perplexity.ts +2 -2
- package/src/providers/integrations/qoder/qoder-catalog.ts +132 -17
- package/src/providers/integrations/qoder/qoder-checkin.ts +26 -8
- package/src/providers/integrations/qoder/qoder-native-identity-store.ts +9 -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 +22 -28
- package/src/providers/integrations/qoder/qoder-sse.ts +159 -0
- package/src/providers/integrations/qoder/qoder-umid.ts +33 -38
- package/src/providers/integrations/qoder/qoder-virtual.ts +14 -2
- package/src/providers/integrations/qoder/qoder.ts +521 -63
- 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/tokenharbor.ts +2 -2
- 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 +59 -19
- package/src/providers/integrations/xai/xai-oauth.ts +5 -5
- package/src/providers/integrations/zcode/oauth.ts +1 -1
- 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 +22 -6
- package/src/providers/operations/provider-credential-service.ts +35 -6
- package/src/providers/operations/upstream-deadline.ts +3 -3
- package/src/providers/provider-contracts.ts +65 -10
- package/src/providers/provider-credential-resolver.ts +1 -1
- 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-registry.ts +4 -4
- package/src/providers/provider-usage.ts +81 -16
- package/src/providers/quota/cache.ts +29 -0
- package/src/providers/quota/quota-contracts.ts +28 -10
- package/src/providers/quota/refresh.ts +29 -6
- package/src/providers/quota/snapshot.ts +94 -0
- package/src/providers/search/search-provider.ts +6 -8
- package/src/providers/search/search-providers.ts +2 -1
- package/src/security/abuse.ts +8 -8
- package/src/security/admission/reasons.ts +17 -17
- package/src/security/admission/service.ts +2 -2
- package/src/security/admission/ttl.ts +61 -61
- package/src/security/api-key-auth.ts +20 -8
- 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/coerce.ts +19 -0
- package/src/shared/invoke-guard.ts +47 -0
- package/src/transport/contracts.ts +13 -4
- package/src/transport/dispatch/attempt-finalize.ts +5 -4
- package/src/transport/dispatch/attempt-loop.ts +10 -7
- package/src/transport/dispatch/capture-policy.ts +34 -37
- package/src/transport/dispatch/empty-stream.ts +28 -25
- package/src/transport/dispatch/generation.ts +33 -53
- package/src/transport/dispatch/leases.ts +8 -8
- package/src/transport/dispatch/native-shared.ts +127 -0
- package/src/transport/dispatch/proxy-request.ts +10 -8
- package/src/transport/dispatch/responses-compact.ts +35 -56
- package/src/transport/dispatch/retry-policy.ts +4 -67
- package/src/transport/dispatch/stream-bridge.ts +1 -1
- package/src/transport/dispatch/streaming/encoder.ts +18 -72
- package/src/transport/dispatch/streaming/finalize.ts +11 -10
- 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 +27 -55
- package/src/transport/dispatch/token-count.ts +49 -46
- package/src/transport/dispatch/websearch.ts +24 -50
- package/src/transport/drain-endpoint.ts +3 -3
- package/src/transport/failure-policy.ts +19 -6
- package/src/transport/guards/auth-guard.ts +6 -11
- package/src/transport/guards/body-policy.ts +7 -7
- package/src/transport/guards/error-lifecycle.ts +18 -3
- package/src/transport/guards/guard-errors.ts +7 -3
- package/src/transport/guards/headroom-guard.ts +2 -2
- package/src/transport/guards/pipeline.ts +7 -7
- package/src/transport/guards/readiness-guard.ts +16 -2
- package/src/transport/guards/request-context.ts +15 -20
- package/src/transport/request/preparer.ts +306 -27
- 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 +5 -20
- 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 +113 -19
- package/src/transport/routing/route-model.ts +131 -38
- package/src/transport/routing/router.ts +154 -39
- package/src/transport/shutdown-notice.ts +21 -9
- 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 +3 -3
- package/src/transport/surface/content-parts.ts +1 -1
- package/src/transport/surface/dialects.ts +2 -2
- package/src/transport/surface/messages/encode.ts +1 -1
- 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-2TJD37Hb.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-xefJNRmJ.js.gz +0 -0
- package/dist/dashboard/assets/CliToolsPage-Rj9iHQ3F.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-JEbYJFxw.js +0 -1
- package/dist/dashboard/assets/CombosPage-JEbYJFxw.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-C3xx_inM.js +0 -1
- package/dist/dashboard/assets/ConsoleLogPage-C3xx_inM.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-Dw306gJU.js +0 -1
- package/dist/dashboard/assets/CustomizationPage-Dw306gJU.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-jaBo1TWV.js +0 -1
- package/dist/dashboard/assets/HealthEventsModal-jaBo1TWV.js.gz +0 -0
- package/dist/dashboard/assets/LoginPage-C4w7vea-.js.gz +0 -0
- package/dist/dashboard/assets/ModelPicker-BtPcAfot.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-CGC5gNs1.js +0 -1
- package/dist/dashboard/assets/OverviewPage-CGC5gNs1.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-DQcthNGt.js +0 -4
- package/dist/dashboard/assets/ProviderDetailPage-DQcthNGt.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-0OsXRGXe.js +0 -1
- package/dist/dashboard/assets/ProvidersPage-0OsXRGXe.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-DdPc8slb.js +0 -9
- package/dist/dashboard/assets/ProxyPage-DdPc8slb.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-Ci5O4BnH.js +0 -1
- package/dist/dashboard/assets/QuotaPage-Ci5O4BnH.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-CDZjTt87.js +0 -1
- package/dist/dashboard/assets/SettingsPage-CDZjTt87.js.gz +0 -0
- package/dist/dashboard/assets/SetupPage-BQS0Rx_u.js.gz +0 -0
- package/dist/dashboard/assets/SortableList-BAL5npBC.js +0 -1
- package/dist/dashboard/assets/SortableList-BAL5npBC.js.gz +0 -0
- package/dist/dashboard/assets/StudioPage-TP7voAQ0.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-Cj_fEzhg.js +0 -56
- package/dist/dashboard/assets/UsagePage-Cj_fEzhg.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-MbI7e7rt.js +0 -1
- package/dist/dashboard/assets/api-keys-MbI7e7rt.js.gz +0 -0
- package/dist/dashboard/assets/app-B2vyPoII.css +0 -2
- package/dist/dashboard/assets/app-B2vyPoII.css.gz +0 -0
- package/dist/dashboard/assets/assert-C0YYuZJ4.js.gz +0 -0
- package/dist/dashboard/assets/badge-B_DMpMJN.js.gz +0 -0
- package/dist/dashboard/assets/banner-DMrMPmth.js.gz +0 -0
- package/dist/dashboard/assets/card-Be5Uzamx.js.gz +0 -0
- package/dist/dashboard/assets/cli-tools-muFRphDu.js.gz +0 -0
- package/dist/dashboard/assets/clipboard-button-DOfrVmsi.js.gz +0 -0
- package/dist/dashboard/assets/dialog-C9omdjQt.js.gz +0 -0
- package/dist/dashboard/assets/drawer-Bm8WKaGY.js.gz +0 -0
- package/dist/dashboard/assets/entry-DS90gUks.js +0 -1
- package/dist/dashboard/assets/entry-DS90gUks.js.gz +0 -0
- package/dist/dashboard/assets/entry-j40LJotO.js +0 -4
- package/dist/dashboard/assets/entry-j40LJotO.js.gz +0 -0
- package/dist/dashboard/assets/entry-q5HwZ8nN.js.gz +0 -0
- package/dist/dashboard/assets/formatters-BTScrQpe.js.gz +0 -0
- package/dist/dashboard/assets/github-badge-Orjuzemr.js.gz +0 -0
- package/dist/dashboard/assets/index-cPrX5vn6.js.gz +0 -0
- package/dist/dashboard/assets/inline-Dvux8w-0.js.gz +0 -0
- package/dist/dashboard/assets/input-CU1Rgf3p.js.gz +0 -0
- package/dist/dashboard/assets/list-row-CzFvogbj.js.gz +0 -0
- package/dist/dashboard/assets/live-KaG15HLQ.js +0 -1
- package/dist/dashboard/assets/live-KaG15HLQ.js.gz +0 -0
- package/dist/dashboard/assets/minimize-2-D-MN8W8a.js +0 -1
- package/dist/dashboard/assets/minimize-2-D-MN8W8a.js.gz +0 -0
- package/dist/dashboard/assets/mutation-JJGQm8TD.js.gz +0 -0
- package/dist/dashboard/assets/numbers-BNDZnsUE.js +0 -2
- package/dist/dashboard/assets/numbers-BNDZnsUE.js.gz +0 -0
- package/dist/dashboard/assets/query-keys-CLaNz8Xz.js.gz +0 -0
- package/dist/dashboard/assets/quota-DZGyp0N5.js +0 -1
- package/dist/dashboard/assets/quota-DZGyp0N5.js.gz +0 -0
- package/dist/dashboard/assets/routing-BEH32QOC.js.gz +0 -0
- package/dist/dashboard/assets/select-B3iTetHN.js.gz +0 -0
- package/dist/dashboard/assets/settings-Dt3Eqbnq.js +0 -1
- package/dist/dashboard/assets/settings-Dt3Eqbnq.js.gz +0 -0
- package/dist/dashboard/assets/stack-KrOrm7JZ.js.gz +0 -0
- package/dist/dashboard/assets/state-DLS7IDMy.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-42ItvbAs.js.gz +0 -0
- package/dist/dashboard/assets/useMutation-Dmnfh1Ue.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-wvwCi4Eh.js → app-Bgd3zANQ.js} +0 -0
- /package/dist/dashboard/assets/{app-wvwCi4Eh.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
|