cartethyia 2.7.4 → 2.7.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +5 -0
- package/README.md +18 -6
- package/dist/dashboard/assets/BannedPage-B1N603cM.js +1 -0
- package/dist/dashboard/assets/BannedPage-B1N603cM.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-9q4_4NoK.js +3 -0
- package/dist/dashboard/assets/CliToolDetailPage-9q4_4NoK.js.gz +0 -0
- package/dist/dashboard/assets/CliToolsPage-DwshGtK-.js +1 -0
- package/dist/dashboard/assets/CliToolsPage-DwshGtK-.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-D762MIkp.js +1 -0
- package/dist/dashboard/assets/CombosPage-D762MIkp.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-KRYjHZsX.js +1 -0
- package/dist/dashboard/assets/ConsoleLogPage-KRYjHZsX.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-DXsxk5uM.js +1 -0
- package/dist/dashboard/assets/CustomizationPage-DXsxk5uM.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-BEjAjD4F.js +1 -0
- package/dist/dashboard/assets/HealthEventsModal-BEjAjD4F.js.gz +0 -0
- package/dist/dashboard/assets/InjectSystemPromptPanel-d5v3K_Yc.js +899 -0
- package/dist/dashboard/assets/InjectSystemPromptPanel-d5v3K_Yc.js.gz +0 -0
- package/dist/dashboard/assets/LoginPage-Ccv4tjNS.js +1 -0
- package/dist/dashboard/assets/LoginPage-Ccv4tjNS.js.gz +0 -0
- package/dist/dashboard/assets/{ModelPicker-BMS2t8BA.js → ModelPicker-C6WlpU5E.js} +1 -1
- package/dist/dashboard/assets/ModelPicker-C6WlpU5E.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-DN9fYNWA.js +1 -0
- package/dist/dashboard/assets/OverviewPage-DN9fYNWA.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-BY0ieUgz.js +5 -0
- package/dist/dashboard/assets/ProviderDetailPage-BY0ieUgz.js.gz +0 -0
- package/dist/dashboard/assets/ProviderIcon-jjqXJDA3.js +1 -0
- package/dist/dashboard/assets/ProviderIcon-jjqXJDA3.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-DNsbfea2.js +1 -0
- package/dist/dashboard/assets/ProvidersPage-DNsbfea2.js.gz +0 -0
- package/dist/dashboard/assets/{ProxyPage-lsrx7NYg.js → ProxyPage-DxjvUzSD.js} +1 -1
- package/dist/dashboard/assets/ProxyPage-DxjvUzSD.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-OaHScjpX.js +1 -0
- package/dist/dashboard/assets/QuotaPage-OaHScjpX.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-BW4AKj2H.js +1 -0
- package/dist/dashboard/assets/SettingsPage-BW4AKj2H.js.gz +0 -0
- package/dist/dashboard/assets/SetupPage-44BZrxFC.js +1 -0
- package/dist/dashboard/assets/SetupPage-44BZrxFC.js.gz +0 -0
- package/dist/dashboard/assets/{StudioPage-Cq5tW7hW.js → StudioPage-DRPavCdU.js} +4 -4
- package/dist/dashboard/assets/StudioPage-DRPavCdU.js.gz +0 -0
- package/dist/dashboard/assets/ToolIcon-CfeLA-Z_.js +1 -0
- package/dist/dashboard/assets/ToolIcon-CfeLA-Z_.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-B7M7LvMM.js +56 -0
- package/dist/dashboard/assets/UsagePage-B7M7LvMM.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-KvX83mRe.js +1 -0
- package/dist/dashboard/assets/api-keys-KvX83mRe.js.gz +0 -0
- package/dist/dashboard/assets/app-DRnQLhsc.js +9 -0
- package/dist/dashboard/assets/app-DRnQLhsc.js.gz +0 -0
- package/dist/dashboard/assets/app-mweRcsKp.css +2 -0
- package/dist/dashboard/assets/app-mweRcsKp.css.gz +0 -0
- package/dist/dashboard/assets/assert-CidQVoNo.js +1 -0
- package/dist/dashboard/assets/assert-CidQVoNo.js.gz +0 -0
- package/dist/dashboard/assets/{badge-D9CS6dZk.js → badge-BaX2GTrn.js} +1 -1
- package/dist/dashboard/assets/badge-BaX2GTrn.js.gz +0 -0
- package/dist/dashboard/assets/{banner-DympBNq4.js → banner-DFH4Bkte.js} +1 -1
- package/dist/dashboard/assets/banner-DFH4Bkte.js.gz +0 -0
- package/dist/dashboard/assets/brand/login_register.jpg +0 -0
- package/dist/dashboard/assets/brand/login_register_initial.jpg +0 -0
- package/dist/dashboard/assets/brand/og_bansos.jpg +0 -0
- package/dist/dashboard/assets/brand/og_image.jpg +0 -0
- package/dist/dashboard/assets/{card-ibRWU_uT.js → card-CxDw0lK6.js} +1 -1
- package/dist/dashboard/assets/card-CxDw0lK6.js.gz +0 -0
- package/dist/dashboard/assets/chevron-left-Bf6CaTSJ.js +1 -0
- package/dist/dashboard/assets/chevron-left-Bf6CaTSJ.js.gz +0 -0
- package/dist/dashboard/assets/circle-question-mark-Cz4un8sH.js +1 -0
- package/dist/dashboard/assets/circle-question-mark-Cz4un8sH.js.gz +0 -0
- package/dist/dashboard/assets/cli-tools-Bwv5-zGZ.js +1 -0
- package/dist/dashboard/assets/cli-tools-Bwv5-zGZ.js.gz +0 -0
- package/dist/dashboard/assets/{clipboard-button-DJm-9pz7.js → clipboard-button-DfS1ntiq.js} +1 -1
- package/dist/dashboard/assets/clipboard-button-DfS1ntiq.js.gz +0 -0
- package/dist/dashboard/assets/dialog-BK-NRr5u.js +1 -0
- package/dist/dashboard/assets/dialog-BK-NRr5u.js.gz +0 -0
- package/dist/dashboard/assets/{drawer-CqGYeOrd.js → drawer-QqPul6sC.js} +1 -1
- package/dist/dashboard/assets/drawer-QqPul6sC.js.gz +0 -0
- package/dist/dashboard/assets/entry-BMlYJVFj.js +1 -0
- package/dist/dashboard/assets/entry-BMlYJVFj.js.gz +0 -0
- package/dist/dashboard/assets/entry-D6WvojeD.js +4 -0
- package/dist/dashboard/assets/entry-D6WvojeD.js.gz +0 -0
- package/dist/dashboard/assets/entry-Dmx7gBiN.js +1 -0
- package/dist/dashboard/assets/entry-Dmx7gBiN.js.gz +0 -0
- package/dist/dashboard/assets/{formatters-D-MTJxMr.js → formatters-u7JPB5BX.js} +1 -1
- package/dist/dashboard/assets/formatters-u7JPB5BX.js.gz +0 -0
- package/dist/dashboard/assets/house-h9AZlJuT.js +1 -0
- package/dist/dashboard/assets/house-h9AZlJuT.js.gz +0 -0
- package/dist/dashboard/assets/{index-DBNPOfhV.js → index-Ko4dLRnV.js} +2 -2
- package/dist/dashboard/assets/index-Ko4dLRnV.js.gz +0 -0
- package/dist/dashboard/assets/{inline-BT0zntEr.js → inline-CHJQwxLa.js} +1 -1
- package/dist/dashboard/assets/inline-CHJQwxLa.js.gz +0 -0
- package/dist/dashboard/assets/{input-Da2ZViWg.js → input-BlmlizVV.js} +1 -1
- package/dist/dashboard/assets/input-BlmlizVV.js.gz +0 -0
- package/dist/dashboard/assets/{layout-BXNeLw6M.js → layout-Bz93I-yE.js} +1 -1
- package/dist/dashboard/assets/layout-Bz93I-yE.js.gz +0 -0
- package/dist/dashboard/assets/{list-row-BycOrrIV.js → list-row-Ml16KCfu.js} +1 -1
- package/dist/dashboard/assets/list-row-Ml16KCfu.js.gz +0 -0
- package/dist/dashboard/assets/live-7_QpnYGu.js +1 -0
- package/dist/dashboard/assets/live-7_QpnYGu.js.gz +0 -0
- package/dist/dashboard/assets/{mutation-Rr0TIvcj.js → mutation-GU7XdEjz.js} +1 -1
- package/dist/dashboard/assets/mutation-GU7XdEjz.js.gz +0 -0
- package/dist/dashboard/assets/network-CuMDaTzT.js +1 -0
- package/dist/dashboard/assets/network-CuMDaTzT.js.gz +0 -0
- package/dist/dashboard/assets/numbers-CoqpaDMx.js +2 -0
- package/dist/dashboard/assets/numbers-CoqpaDMx.js.gz +0 -0
- package/dist/dashboard/assets/pagination-B5aOoweZ.js +1 -0
- package/dist/dashboard/assets/pagination-B5aOoweZ.js.gz +0 -0
- package/dist/dashboard/assets/providers/zai.webp +0 -0
- package/dist/dashboard/assets/providers/zcode.webp +0 -0
- package/dist/dashboard/assets/{query-keys-BloKtaXJ.js → query-keys-YSoQOUh6.js} +1 -1
- package/dist/dashboard/assets/{query-keys-BloKtaXJ.js.gz → query-keys-YSoQOUh6.js.gz} +0 -0
- package/dist/dashboard/assets/quota-DJ1QlAOq.js +1 -0
- package/dist/dashboard/assets/quota-DJ1QlAOq.js.gz +0 -0
- package/dist/dashboard/assets/routing-fnCsxgNO.js +1 -0
- package/dist/dashboard/assets/routing-fnCsxgNO.js.gz +0 -0
- package/dist/dashboard/assets/{select-BIYIBkMU.js → select-BHqpd-bg.js} +2 -2
- package/dist/dashboard/assets/select-BHqpd-bg.js.gz +0 -0
- package/dist/dashboard/assets/settings-BxslsErn.js +1 -0
- package/dist/dashboard/assets/settings-BxslsErn.js.gz +0 -0
- package/dist/dashboard/assets/shield-BMQNxORh.js +1 -0
- package/dist/dashboard/assets/shield-BMQNxORh.js.gz +0 -0
- package/dist/dashboard/assets/{stack-DfgnFDWY.js → stack-j7cXjgYX.js} +1 -1
- package/dist/dashboard/assets/stack-j7cXjgYX.js.gz +0 -0
- package/dist/dashboard/assets/{state-CkfdYWel.js → state-DHmEnLa2.js} +1 -1
- package/dist/dashboard/assets/state-DHmEnLa2.js.gz +0 -0
- package/dist/dashboard/assets/{switch-SYm237HG.js → switch-DmZcyTIC.js} +1 -1
- package/dist/dashboard/assets/switch-DmZcyTIC.js.gz +0 -0
- package/dist/dashboard/assets/{useMutation-BQqCRNIi.js → useMutation-CbCP4w1o.js} +1 -1
- package/dist/dashboard/assets/useMutation-CbCP4w1o.js.gz +0 -0
- package/dist/dashboard/console-backgrounds.jpg +0 -0
- package/dist/dashboard/console-backgrounds_initial.jpg +0 -0
- package/dist/dashboard/index.html +2 -2
- package/dist/dashboard/index.html.gz +0 -0
- package/dist/dashboard/precompressed-assets.json +60 -59
- package/migrations/full-postgres/0036_system_prompt_presets_refs.sql +10 -0
- package/migrations/full-postgres/0037_share_concurrency_scope.sql +3 -0
- package/migrations/full-postgres/0038_add_accounts_quota_snapshot.sql +7 -0
- package/migrations/full-postgres/0039_add_telemetry_provider_model_indexes.sql +13 -0
- package/migrations/full-postgres/0040_add_models_dialect.sql +3 -0
- package/migrations/full-postgres/0041_add_daily_checkin_ledger_account_fk.sql +7 -0
- package/migrations/full-postgres/0042_add_telemetry_degenerate_stop.sql +14 -0
- package/migrations/lite-sqlite3/0036_system_prompt_presets_refs.sql +14 -0
- package/migrations/lite-sqlite3/0037_share_concurrency_scope.sql +3 -0
- package/migrations/lite-sqlite3/0038_add_accounts_quota_snapshot.sql +4 -0
- package/migrations/lite-sqlite3/0039_add_telemetry_provider_model_indexes.sql +6 -0
- package/migrations/lite-sqlite3/0040_add_models_dialect.sql +3 -0
- package/migrations/lite-sqlite3/0041_add_daily_checkin_ledger_account_fk.sql +26 -0
- package/migrations/lite-sqlite3/0042_add_telemetry_degenerate_stop.sql +12 -0
- package/package.json +4 -6
- package/scripts/cli/update.ts +15 -41
- package/src/boot/app.ts +58 -2
- package/src/boot/dependencies.ts +794 -732
- package/src/boot/lifecycle.ts +11 -12
- package/src/config/cooldowns.ts +34 -55
- package/src/config/network.ts +1 -1
- package/src/config/package-paths.ts +1 -1
- package/src/config/providers.ts +19 -14
- package/src/config/resources.ts +0 -5
- package/src/config/runtime.ts +2 -5
- package/src/config/spec.ts +33 -6
- package/src/config/usage.ts +27 -0
- package/src/console/api-keys/ban-constants.ts +38 -0
- package/src/console/api-keys/contracts.ts +71 -62
- package/src/console/api-keys/routes.ts +56 -24
- package/src/console/auth/access.ts +4 -4
- package/src/console/auth/account-reset.ts +1 -1
- package/src/console/auth/audit-recorder.ts +1 -1
- package/src/console/auth/lockout.ts +7 -3
- package/src/console/auth/row-coercion.ts +57 -12
- package/src/console/auth/session-resolver.ts +1 -1
- package/src/console/auth/session-service.ts +8 -2
- package/src/console/auth/session.ts +37 -31
- package/src/console/auth/setup-service.ts +4 -3
- package/src/console/auth/user-store.ts +1 -1
- package/src/console/backup/9router-map.ts +0 -3
- package/src/console/backup/contracts.ts +9 -9
- package/src/console/backup/export-endpoint.ts +5 -6
- package/src/console/backup/nine-router.ts +1 -7
- package/src/console/backup/routes.ts +29 -33
- package/src/console/backup/service.ts +468 -468
- package/src/console/backup/store.ts +47 -29
- package/src/console/backup/validate.ts +11 -1
- package/src/console/cli-tools/contracts.ts +43 -299
- package/src/console/cli-tools/fs-ops.ts +29 -0
- package/src/console/cli-tools/injectors/claude.ts +3 -3
- package/src/console/cli-tools/injectors/codex.ts +181 -181
- package/src/console/cli-tools/injectors/driver.ts +150 -247
- package/src/console/cli-tools/injectors/kilo.ts +146 -21
- package/src/console/cli-tools/injectors/opencode.ts +176 -153
- package/src/console/cli-tools/routes.ts +7 -1
- package/src/console/cli-tools/service.ts +8 -7
- package/src/console/cli-tools/store.ts +2 -2
- package/src/console/console-router.ts +32 -0
- package/src/console/dashboard-assets.ts +9 -9
- package/src/console/domain-registration.ts +16 -2
- package/src/console/inferhub/client.ts +0 -7
- package/src/console/inferhub/key-store.ts +1 -1
- package/src/console/inferhub/normalize.ts +2 -2
- package/src/console/inferhub/routes.ts +1 -1
- package/src/console/inferhub/types.ts +1 -1
- package/src/console/observability/contracts.ts +7 -1
- package/src/console/observability/live.ts +31 -18
- package/src/console/observability/logs.ts +1 -1
- package/src/console/observability/sse.ts +62 -8
- package/src/console/observability/store.ts +23 -4
- package/src/console/providers/catalog/catalog-projections.ts +1 -1
- package/src/console/providers/catalog/contracts.ts +39 -0
- package/src/console/providers/catalog/model-operations.ts +1 -1
- package/src/console/providers/catalog/provider-operations.ts +95 -17
- package/src/console/providers/catalog/public-model-store.ts +0 -0
- package/src/console/providers/catalog/routes.ts +27 -3
- package/src/console/providers/catalog/store.ts +147 -8
- package/src/console/providers/detail/routes.ts +1 -1
- package/src/console/providers/oauth/callback-listener.ts +100 -18
- package/src/console/providers/oauth/routes.ts +92 -13
- package/src/console/quota/account-quota-global.ts +41 -3
- package/src/console/quota/account-quota-shared.ts +10 -3
- package/src/console/quota/account-quota-tenant.ts +259 -66
- package/src/console/quota/daily-growth-job.ts +308 -0
- package/src/console/quota/native-identity.ts +2 -2
- package/src/console/quota/quota-view.ts +1 -1
- package/src/console/remote-support/routes.ts +1 -1
- package/src/console/remote-support/service.ts +2 -2
- package/src/console/routing/model/contracts.ts +1 -1
- package/src/console/routing/pools/contracts.ts +2 -2
- package/src/console/routing/pools/import-parse.ts +3 -3
- package/src/console/routing/pools/relay-deploy.ts +3 -3
- package/src/console/settings/contracts.ts +29 -2
- package/src/console/settings/store.ts +20 -0
- package/src/console/share/share-router.ts +100 -32
- package/src/console/share/share-stats.ts +11 -13
- package/src/console/share/share-usage.ts +1 -1
- package/src/console/shared/errors.ts +2 -2
- package/src/console/shared/object.ts +18 -0
- package/src/console/shared/read-through-cache.ts +4 -4
- package/src/console/shared/transaction.ts +14 -0
- package/src/console/system-prompt/contracts.ts +89 -12
- package/src/console/system-prompt/routes.ts +98 -45
- package/src/console/updates/apply-update.ts +244 -0
- package/src/console/updates/routes.ts +252 -97
- package/src/main.ts +11 -5
- package/src/network/http2-bidi.ts +1 -1
- package/src/network/ip-utils.ts +298 -0
- package/src/network/outbound-fetch.ts +1 -1
- package/src/network/pool/agent.ts +7 -6
- package/src/network/pool/byte-accounting.ts +1 -1
- package/src/network/pool/resolver.ts +16 -12
- package/src/network/pool/selector.ts +150 -136
- package/src/network/pool-health-machine.ts +1 -1
- package/src/network/rate-limit-verdict.ts +1 -1
- package/src/network/retry.ts +1 -1
- package/src/network/ssrf.ts +20 -71
- package/src/observability/event-log.ts +9 -2
- package/src/observability/event-query.ts +61 -291
- package/src/observability/log-sanitize.ts +1 -1
- package/src/observability/metrics.ts +4 -4
- package/src/observability/payload-capture.ts +6 -5
- package/src/observability/payload-store.ts +1 -1
- package/src/observability/runtime-metrics.ts +1 -1
- package/src/observability/telemetry-buffer.ts +19 -3
- package/src/observability/telemetry-reconciliation.ts +3 -3
- package/src/observability/token-speed.ts +1 -1
- package/src/persistence/connection-url.ts +1 -1
- package/src/persistence/db-handle.ts +1 -1
- package/src/persistence/db-pg.ts +2 -2
- package/src/persistence/db-sqlite.ts +3 -3
- package/src/persistence/dialect.ts +5 -1
- package/src/persistence/readiness.ts +1 -1
- package/src/persistence/redis.ts +1 -1
- package/src/persistence/schema.ts +121 -40
- package/src/persistence/shared-cache.ts +2 -2
- package/src/persistence/sort-order.ts +1 -1
- package/src/persistence/stores/api-key-store.ts +29 -2
- package/src/persistence/stores/share-store.ts +20 -8
- package/src/persistence/system-prompt-store.ts +63 -18
- package/src/persistence/tenant-preferences.ts +1 -1
- package/src/persistence/ttl-cache.ts +3 -3
- package/src/persistence/usage-flush.ts +7 -2
- package/src/protocol/canonical-model.ts +1 -1
- package/src/protocol/codecs.ts +6 -10
- package/src/protocol/dsml.ts +8 -125
- package/src/protocol/image-clamp.ts +2 -2
- package/src/protocol/reasoning-text.ts +1 -1
- package/src/protocol/reasoning.ts +7 -52
- package/src/protocol/registry.ts +50 -15
- package/src/protocol/request/chat.ts +46 -19
- package/src/protocol/request/codex.ts +1 -1
- package/src/protocol/request/messages.ts +46 -14
- package/src/protocol/request/responses.ts +19 -10
- package/src/protocol/response/chat.ts +25 -10
- package/src/protocol/response/codex.ts +1 -1
- package/src/protocol/response/messages.ts +1 -1
- package/src/protocol/response/omp-tool-call-markup.ts +1 -1
- package/src/protocol/response/responses.ts +1 -1
- package/src/protocol/sse-decoder.ts +50 -1
- package/src/protocol/stop-reason.ts +2 -2
- package/src/protocol/think-split.ts +347 -0
- package/src/protocol/translation/attachment-integrity.ts +3 -3
- package/src/protocol/translation/cache-controls.ts +8 -20
- package/src/protocol/translation/capabilities.ts +6 -6
- package/src/protocol/translation/dialect/deepseek.ts +10 -0
- package/src/protocol/translation/dialect/inband.ts +39 -0
- package/src/protocol/translation/dialect/model-thinking.ts +89 -0
- package/src/protocol/translation/dialect/qwen.ts +114 -0
- package/src/protocol/translation/dialect/resolver.ts +83 -0
- package/src/protocol/translation/dialect/rules.ts +93 -0
- package/src/protocol/translation/dialect/thinking-ladder.ts +245 -0
- package/src/protocol/translation/dialect/types.ts +58 -0
- package/src/protocol/translation/quirks.ts +18 -4
- package/src/protocol/translation/thinking.ts +12 -252
- package/src/protocol/translation/tool-id.ts +3 -3
- package/src/protocol/translation/tool-repair.ts +1 -1
- package/src/protocol/upstream/post-json.ts +6 -1
- package/src/providers/adapters/compatible-adapter.ts +71 -4
- package/src/providers/authentication/jwt-validator.ts +0 -5
- package/src/providers/authentication/oauth-client.ts +254 -253
- package/src/providers/authentication/oauth-flow-store.ts +42 -7
- package/src/providers/bundled-discovery-providers.ts +6 -30
- package/src/providers/configured-openai-providers.ts +82 -97
- package/src/providers/default-registry.ts +35 -37
- package/src/providers/discovery/catalog/catalog-format.ts +13 -2
- package/src/providers/discovery/discovery-types.ts +0 -1
- package/src/providers/discovery/openai-model-discovery.ts +2 -2
- package/src/providers/discovery/probe-phases.ts +48 -13
- package/src/providers/discovery/probing-service.ts +6 -8
- package/src/providers/integrations/agentrouter/agentrouter.ts +12 -2
- package/src/providers/integrations/antigravity/antigravity-oauth.ts +0 -1
- package/src/providers/integrations/antigravity/antigravity-protocol.ts +88 -28
- package/src/providers/integrations/antigravity/antigravity-quota.ts +18 -6
- package/src/providers/integrations/antigravity/antigravity.ts +22 -13
- package/src/providers/integrations/buddy/buddy-catalog-shared.ts +1 -1
- package/src/providers/integrations/buddy/buddy-chat-shared.ts +20 -4
- package/src/providers/integrations/buddy/buddy-cli-headers.ts +19 -52
- package/src/providers/integrations/buddy/buddy-identity.ts +86 -0
- package/src/providers/integrations/buddy/buddy-oauth-shared.ts +1 -1
- package/src/providers/integrations/buddy/codebuddy/cn-provider.ts +1 -2
- package/src/providers/integrations/buddy/codebuddy/provider.ts +4 -15
- package/src/providers/integrations/buddy/codebuddy/shared.ts +30 -9
- package/src/providers/integrations/buddy/workbuddy/provider.ts +5 -9
- package/src/providers/integrations/buddy/workbuddy/shared.ts +21 -3
- package/src/providers/integrations/claude/claude-cch.ts +148 -148
- package/src/providers/integrations/claude/fallback-credit.ts +3 -7
- package/src/providers/integrations/cline/cline.ts +1 -1
- package/src/providers/integrations/codex/codex-device-code.ts +101 -101
- package/src/providers/integrations/codex/codex-oauth.ts +0 -1
- package/src/providers/integrations/commandcode/commandcode.ts +75 -115
- package/src/providers/integrations/commandcode/quota.ts +131 -51
- package/src/providers/integrations/cursor/cursor-http2.ts +0 -3
- package/src/providers/integrations/cursor/cursor-quota.ts +2 -6
- package/src/providers/integrations/cursor/cursor-request.ts +0 -87
- package/src/providers/integrations/cursor/cursor-shared.ts +0 -4
- package/src/providers/integrations/cursor/cursor.ts +35 -3
- package/src/providers/integrations/cursor/protocol/framing.ts +0 -56
- package/src/providers/integrations/cursor/protocol/messages.ts +0 -96
- package/src/providers/integrations/factory/factory.ts +9 -9
- package/src/providers/integrations/github/github-discovery.ts +70 -12
- package/src/providers/integrations/github/github-oauth.ts +184 -14
- package/src/providers/integrations/github/github.ts +134 -26
- package/src/providers/integrations/grok/grok-oauth.ts +261 -261
- package/src/providers/integrations/grok/grok-quota-frame.ts +1 -1
- package/src/providers/integrations/grok/grok-quota.ts +16 -7
- package/src/providers/integrations/grok/grok-ratelimit-store.ts +0 -5
- package/src/providers/integrations/grok/grok-ratelimit.ts +3 -0
- package/src/providers/integrations/grok/grok.ts +86 -36
- package/src/providers/integrations/kilo/kilo-oauth.ts +1 -1
- package/src/providers/integrations/kiro/kiro-cache.ts +31 -0
- package/src/providers/integrations/kiro/kiro-catalog.ts +31 -5
- package/src/providers/integrations/kiro/kiro-discovery.ts +47 -43
- package/src/providers/integrations/kiro/kiro-machine-id.ts +6 -3
- package/src/providers/integrations/kiro/kiro-oauth.ts +523 -255
- package/src/providers/integrations/kiro/kiro-quota.ts +35 -47
- package/src/providers/integrations/kiro/kiro-request.ts +8 -11
- package/src/providers/integrations/kiro/kiro-stream.ts +13 -0
- package/src/providers/integrations/kiro/kiro.ts +80 -68
- package/src/providers/integrations/muse/muse-oauth.ts +0 -1
- package/src/providers/integrations/muse/muse-quota.ts +0 -4
- package/src/providers/integrations/opencode/fingerprint.ts +7 -5
- package/src/providers/integrations/opencode/provider.ts +17 -24
- package/src/providers/integrations/qoder/qoder-catalog.ts +94 -11
- package/src/providers/integrations/qoder/qoder-oauth.ts +165 -25
- package/src/providers/integrations/qoder/qoder-quota.ts +48 -6
- package/src/providers/integrations/qoder/qoder-realm.ts +10 -31
- package/src/providers/integrations/qoder/qoder-sse.ts +159 -0
- package/src/providers/integrations/qoder/qoder-umid.ts +21 -25
- package/src/providers/integrations/qoder/qoder-virtual.ts +12 -0
- package/src/providers/integrations/qoder/qoder.ts +422 -71
- package/src/providers/integrations/shared/connect.ts +0 -54
- package/src/providers/integrations/shared/hash.ts +15 -0
- package/src/providers/integrations/shared/session-cache.ts +136 -0
- package/src/providers/integrations/trae/trae-cache.ts +7 -87
- package/src/providers/integrations/trae/trae-catalog.ts +1 -6
- package/src/providers/integrations/trae/trae-protocol.ts +17 -8
- package/src/providers/integrations/trae/trae-realm.ts +0 -7
- package/src/providers/integrations/trae/trae.ts +51 -8
- package/src/providers/integrations/xai/xai-oauth.ts +5 -5
- package/src/providers/integrations/zcode-start-plan/gifts.ts +265 -0
- package/src/providers/integrations/zcode-start-plan/oauth.ts +154 -0
- package/src/providers/integrations/zcode-start-plan/provider.ts +131 -0
- package/src/providers/integrations/zcode-start-plan/quota.ts +152 -0
- package/src/providers/integrations/zcode-start-plan/zcode_prompt.json +10 -0
- package/src/providers/operations/account-error-classification.ts +198 -133
- package/src/providers/operations/account-health-service.ts +31 -39
- package/src/providers/operations/account-reset-service.ts +58 -1
- package/src/providers/operations/byok-wire-profile.ts +19 -15
- package/src/providers/operations/cli-platform.ts +31 -5
- package/src/providers/operations/client-version-resolver.ts +4 -6
- package/src/providers/operations/client-versions.ts +10 -7
- package/src/providers/operations/provider-catalog-seeder.ts +2 -0
- package/src/providers/operations/provider-catalog-service.ts +18 -2
- package/src/providers/operations/provider-credential-service.ts +19 -2
- package/src/providers/provider-contracts.ts +18 -7
- package/src/providers/provider-metadata.ts +17 -2
- package/src/providers/provider-model-definition.ts +22 -1
- package/src/providers/provider-probe.ts +4 -2
- package/src/providers/provider-usage.ts +50 -19
- package/src/providers/quota/cache.ts +29 -0
- package/src/providers/quota/quota-contracts.ts +4 -0
- package/src/providers/quota/refresh.ts +27 -4
- package/src/providers/quota/snapshot.ts +94 -0
- package/src/providers/search/search-provider.ts +1 -3
- package/src/providers/search/search-providers.ts +2 -1
- package/src/security/abuse.ts +4 -4
- package/src/security/admission/service.ts +13 -6
- package/src/security/admission/ttl.ts +61 -61
- package/src/security/api-key-auth.ts +17 -3
- package/src/security/client-router-fingerprint.ts +4 -4
- package/src/security/credential-codec.ts +3 -3
- package/src/security/csrf.ts +2 -2
- package/src/security/ip-boundary.ts +16 -189
- package/src/security/model-access-rule.ts +1 -1
- package/src/security/outbound-headers.ts +18 -0
- package/src/shared/invoke-guard.ts +47 -0
- package/src/transport/contracts.ts +13 -4
- package/src/transport/dispatch/attempt-finalize.ts +70 -22
- package/src/transport/dispatch/attempt-loop.ts +22 -0
- package/src/transport/dispatch/capture-policy.ts +34 -37
- package/src/transport/dispatch/empty-stream.ts +23 -20
- package/src/transport/dispatch/generation.ts +25 -45
- package/src/transport/dispatch/native-shared.ts +127 -0
- package/src/transport/dispatch/proxy-request.ts +3 -1
- package/src/transport/dispatch/responses-compact.ts +25 -46
- package/src/transport/dispatch/streaming/encoder.ts +18 -72
- package/src/transport/dispatch/streaming/finalize.ts +2 -2
- package/src/transport/dispatch/streaming/stream.ts +7 -3
- package/src/transport/dispatch/streaming/thinking-loop.ts +1 -1
- package/src/transport/dispatch/systemone.ts +20 -48
- package/src/transport/dispatch/token-count.ts +41 -38
- package/src/transport/dispatch/websearch.ts +19 -45
- package/src/transport/drain-endpoint.ts +1 -1
- package/src/transport/failure-policy.ts +21 -1
- package/src/transport/guards/auth-guard.ts +0 -6
- package/src/transport/guards/body-policy.ts +3 -3
- package/src/transport/guards/error-lifecycle.ts +4 -0
- package/src/transport/guards/headroom-guard.ts +1 -1
- package/src/transport/guards/pipeline.ts +1 -1
- package/src/transport/guards/request-context.ts +12 -17
- package/src/transport/request/preparer.ts +286 -7
- package/src/transport/request/rtk/compress-request.ts +3 -3
- package/src/transport/request/rtk/rtk-constants.ts +2 -2
- package/src/transport/request/rtk/rtk-filters.ts +8 -8
- package/src/transport/request/state.ts +7 -13
- package/src/transport/request/system-prompt-injection.ts +74 -11
- package/src/transport/request/tenant-preferences.ts +22 -0
- package/src/transport/routing/candidate-index.ts +2 -2
- package/src/transport/routing/round-robin.ts +1 -1
- package/src/transport/routing/route-catalog.ts +17 -18
- package/src/transport/routing/route-model.ts +126 -33
- package/src/transport/routing/router.ts +159 -37
- package/src/transport/shutdown-notice.ts +2 -2
- package/src/transport/surface/adapters.ts +3 -3
- package/src/transport/surface/chat/encode.ts +7 -7
- package/src/transport/surface/chat/parse.ts +7 -7
- package/src/transport/surface/completion.ts +2 -2
- package/src/transport/surface/content-parts.ts +1 -1
- package/src/transport/surface/dialects.ts +2 -2
- package/src/transport/surface/messages/parse.ts +3 -3
- package/src/transport/surface/reasoning-summary.ts +9 -14
- package/src/transport/surface/responses/adapter.ts +1 -1
- package/src/transport/surface/responses/errors.ts +0 -9
- package/src/transport/vision-handoff-cache.ts +58 -0
- package/src/transport/vision-handoff.ts +431 -0
- package/src/workers/account-identity-sweep.ts +2 -2
- package/src/workers/buddy-growth-tasks.ts +392 -346
- package/src/workers/daily-checkin.ts +102 -49
- package/src/workers/maintenance-lease.ts +2 -35
- package/src/workers/oauth-refresh-sweep.ts +8 -1
- package/src/workers/quota-refresh-sweep.ts +120 -82
- package/src/workers/scheduler.ts +67 -4
- package/src/workers/sweep-runner.ts +24 -5
- package/src/workers/zcode-gift-claim.ts +108 -0
- package/dist/dashboard/assets/AccountCooldown-Bjo92Qat.js +0 -1
- package/dist/dashboard/assets/AccountCooldown-Bjo92Qat.js.gz +0 -0
- package/dist/dashboard/assets/BannedPage-ChG5svFb.js +0 -1
- package/dist/dashboard/assets/BannedPage-ChG5svFb.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-BIcWQun7.js +0 -1
- package/dist/dashboard/assets/CliToolDetailPage-BIcWQun7.js.gz +0 -0
- package/dist/dashboard/assets/CliToolsPage-Bf2VelDe.js +0 -1
- package/dist/dashboard/assets/CliToolsPage-Bf2VelDe.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-DCgm5g6g.js +0 -1
- package/dist/dashboard/assets/CombosPage-DCgm5g6g.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-a8mD0QF1.js +0 -1
- package/dist/dashboard/assets/ConsoleLogPage-a8mD0QF1.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-ggeMSjte.js +0 -1
- package/dist/dashboard/assets/CustomizationPage-ggeMSjte.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-DzSWzE_j.js +0 -1
- package/dist/dashboard/assets/HealthEventsModal-DzSWzE_j.js.gz +0 -0
- package/dist/dashboard/assets/LoginPage-U6AKaJcF.js +0 -1
- package/dist/dashboard/assets/LoginPage-U6AKaJcF.js.gz +0 -0
- package/dist/dashboard/assets/ModelPicker-BMS2t8BA.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-D2D4IiHl.js +0 -1
- package/dist/dashboard/assets/OverviewPage-D2D4IiHl.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-CrNf7j_X.js +0 -5
- package/dist/dashboard/assets/ProviderDetailPage-CrNf7j_X.js.gz +0 -0
- package/dist/dashboard/assets/ProviderIcon-FB6yS4v1.js +0 -1
- package/dist/dashboard/assets/ProviderIcon-FB6yS4v1.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-BSDbquc1.js +0 -1
- package/dist/dashboard/assets/ProvidersPage-BSDbquc1.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-lsrx7NYg.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-L0skzML8.js +0 -1
- package/dist/dashboard/assets/QuotaPage-L0skzML8.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-CmAbjRHW.js +0 -1
- package/dist/dashboard/assets/SettingsPage-CmAbjRHW.js.gz +0 -0
- package/dist/dashboard/assets/SetupPage-f0DE1h0-.js +0 -1
- package/dist/dashboard/assets/SetupPage-f0DE1h0-.js.gz +0 -0
- package/dist/dashboard/assets/SortableList-ClzpwyET.js +0 -1
- package/dist/dashboard/assets/SortableList-ClzpwyET.js.gz +0 -0
- package/dist/dashboard/assets/StudioPage-Cq5tW7hW.js.gz +0 -0
- package/dist/dashboard/assets/ToolIcon-K1ckcDKg.js +0 -1
- package/dist/dashboard/assets/ToolIcon-K1ckcDKg.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-Dfc9rWQn.js +0 -56
- package/dist/dashboard/assets/UsagePage-Dfc9rWQn.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-C38iXoO4.js +0 -1
- package/dist/dashboard/assets/api-keys-C38iXoO4.js.gz +0 -0
- package/dist/dashboard/assets/app-BayTTy01.js +0 -9
- package/dist/dashboard/assets/app-BayTTy01.js.gz +0 -0
- package/dist/dashboard/assets/app-a159k0ub.css +0 -2
- package/dist/dashboard/assets/app-a159k0ub.css.gz +0 -0
- package/dist/dashboard/assets/assert-DbRA8EG2.js +0 -15
- package/dist/dashboard/assets/assert-DbRA8EG2.js.gz +0 -0
- package/dist/dashboard/assets/badge-D9CS6dZk.js.gz +0 -0
- package/dist/dashboard/assets/banner-DympBNq4.js.gz +0 -0
- package/dist/dashboard/assets/card-ibRWU_uT.js.gz +0 -0
- package/dist/dashboard/assets/cli-tools-63qBGaRw.js +0 -1
- package/dist/dashboard/assets/cli-tools-63qBGaRw.js.gz +0 -0
- package/dist/dashboard/assets/clipboard-button-DJm-9pz7.js.gz +0 -0
- package/dist/dashboard/assets/dialog-8fiE6tec.js +0 -1
- package/dist/dashboard/assets/dialog-8fiE6tec.js.gz +0 -0
- package/dist/dashboard/assets/drawer-CqGYeOrd.js.gz +0 -0
- package/dist/dashboard/assets/entry-BSAsYsmV.js +0 -1
- package/dist/dashboard/assets/entry-BSAsYsmV.js.gz +0 -0
- package/dist/dashboard/assets/entry-CiIQULZD.js +0 -4
- package/dist/dashboard/assets/entry-CiIQULZD.js.gz +0 -0
- package/dist/dashboard/assets/entry-Daz0lJ-W.js +0 -1
- package/dist/dashboard/assets/entry-Daz0lJ-W.js.gz +0 -0
- package/dist/dashboard/assets/formatters-D-MTJxMr.js.gz +0 -0
- package/dist/dashboard/assets/github-badge-CPK47xQx.js +0 -1
- package/dist/dashboard/assets/github-badge-CPK47xQx.js.gz +0 -0
- package/dist/dashboard/assets/index-DBNPOfhV.js.gz +0 -0
- package/dist/dashboard/assets/inline-BT0zntEr.js.gz +0 -0
- package/dist/dashboard/assets/input-Da2ZViWg.js.gz +0 -0
- package/dist/dashboard/assets/layout-BXNeLw6M.js.gz +0 -0
- package/dist/dashboard/assets/list-row-BycOrrIV.js.gz +0 -0
- package/dist/dashboard/assets/live-DCQNZpF7.js +0 -1
- package/dist/dashboard/assets/live-DCQNZpF7.js.gz +0 -0
- package/dist/dashboard/assets/mutation-Rr0TIvcj.js.gz +0 -0
- package/dist/dashboard/assets/network-9Yj2ZyLo.js +0 -1
- package/dist/dashboard/assets/network-9Yj2ZyLo.js.gz +0 -0
- package/dist/dashboard/assets/numbers-CEagj6ZZ.js +0 -2
- package/dist/dashboard/assets/numbers-CEagj6ZZ.js.gz +0 -0
- package/dist/dashboard/assets/providers/zai-coding-plan.svg +0 -6
- package/dist/dashboard/assets/providers/zai-coding-plan.svg.gz +0 -0
- package/dist/dashboard/assets/quota-Caw7wqci.js +0 -1
- package/dist/dashboard/assets/quota-Caw7wqci.js.gz +0 -0
- package/dist/dashboard/assets/routing-CO9zCUus.js +0 -1
- package/dist/dashboard/assets/routing-CO9zCUus.js.gz +0 -0
- package/dist/dashboard/assets/select-BIYIBkMU.js.gz +0 -0
- package/dist/dashboard/assets/settings-jS3-PNSA.js +0 -1
- package/dist/dashboard/assets/settings-jS3-PNSA.js.gz +0 -0
- package/dist/dashboard/assets/stack-DfgnFDWY.js.gz +0 -0
- package/dist/dashboard/assets/state-CkfdYWel.js.gz +0 -0
- package/dist/dashboard/assets/stream-retry-5QnVuSSo.js +0 -1
- package/dist/dashboard/assets/stream-retry-5QnVuSSo.js.gz +0 -0
- package/dist/dashboard/assets/switch-SYm237HG.js.gz +0 -0
- package/dist/dashboard/assets/useMutation-BQqCRNIi.js.gz +0 -0
- package/src/console/cli-tools/injectors/cline.ts +0 -110
- package/src/console/cli-tools/injectors/copilot.ts +0 -108
- package/src/console/cli-tools/injectors/cowork.ts +0 -64
- package/src/console/cli-tools/injectors/deepseek-tui.ts +0 -85
- package/src/console/cli-tools/injectors/droid.ts +0 -96
- package/src/console/cli-tools/injectors/grok-build.ts +0 -70
- package/src/console/cli-tools/injectors/hermes.ts +0 -101
- package/src/console/cli-tools/injectors/jcode.ts +0 -115
- package/src/console/cli-tools/injectors/openclaw.ts +0 -161
- package/src/network/response-headers.ts +0 -39
- package/src/protocol/response/dsml.ts +0 -38
- package/src/providers/integrations/xiaomi-mimo/think-stream.ts +0 -229
- /package/dist/dashboard/assets/brand/{og_bansos.webp → og_bansos_initial.webp} +0 -0
- /package/dist/dashboard/assets/brand/{og_image.webp → og_image_initial.webp} +0 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-process cache of vision-handoff descriptions.
|
|
3
|
+
*
|
|
4
|
+
* The key is a deterministic hash of the *set* of image payloads the handoff
|
|
5
|
+
* actually described (the last image-carrying user turn). Re-sending the same
|
|
6
|
+
* attachment — the same image in a later turn, or the same conversation
|
|
7
|
+
* replayed — skips a second vision call entirely. Bounded and TTL'd so it can
|
|
8
|
+
* never grow without limit.
|
|
9
|
+
*
|
|
10
|
+
* The cache is keyed by the active image set rather than a single image
|
|
11
|
+
* because one vision model call describes the whole set in a single block of
|
|
12
|
+
* text; you cannot reliably split that block back into per-image descriptions.
|
|
13
|
+
* A repeated single-image request still hits, as does any repeated set.
|
|
14
|
+
*/
|
|
15
|
+
import { createHash } from "node:crypto";
|
|
16
|
+
|
|
17
|
+
const CACHE_MAX_ENTRIES = 512;
|
|
18
|
+
const CACHE_TTL_MS = 24 * 60 * 60 * 1000;
|
|
19
|
+
|
|
20
|
+
interface CacheEntry {
|
|
21
|
+
readonly description: string;
|
|
22
|
+
readonly at: number;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const store = new Map<string, CacheEntry>();
|
|
26
|
+
|
|
27
|
+
/** Deterministic hash of one canonical image part's payload. */
|
|
28
|
+
export function hashImagePart(part: { kind: "image"; payload: unknown }): string {
|
|
29
|
+
const payload =
|
|
30
|
+
typeof part.payload === "string" ? part.payload : JSON.stringify(part.payload);
|
|
31
|
+
return createHash("sha256").update(`image:${payload}`).digest("hex");
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Look up a previously stored description for an active image set hash. */
|
|
35
|
+
export function getCachedVisionDescription(hash: string): string | undefined {
|
|
36
|
+
const entry = store.get(hash);
|
|
37
|
+
if (entry === undefined) return undefined;
|
|
38
|
+
if (Date.now() - entry.at > CACHE_TTL_MS) {
|
|
39
|
+
store.delete(hash);
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
return entry.description;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Clear the vision-handoff description cache (test isolation). */
|
|
46
|
+
export function clearCachedVisionDescriptions(): void {
|
|
47
|
+
store.clear();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Store the description produced for an active image set hash. */
|
|
51
|
+
export function setCachedVisionDescription(hash: string, description: string): void {
|
|
52
|
+
store.set(hash, { description, at: Date.now() });
|
|
53
|
+
// Evict the oldest (first-inserted) entry when over the cap.
|
|
54
|
+
if (store.size > CACHE_MAX_ENTRIES) {
|
|
55
|
+
const oldestKey = store.keys().next().value as string | undefined;
|
|
56
|
+
if (oldestKey !== undefined) store.delete(oldestKey);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,431 @@
|
|
|
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 { estimateInputTokens } from "./request/token-estimate";
|
|
31
|
+
import { getCachedVisionDescription, setCachedVisionDescription } from "./vision-handoff-cache";
|
|
32
|
+
import { modelsDevCatalog } from "../providers/discovery/models-dev-catalog";
|
|
33
|
+
|
|
34
|
+
/** Dependency surface needed to run a vision handoff (a gateway-handler deps). */
|
|
35
|
+
export interface VisionHandoffDeps extends GatewayHandlerDeps {
|
|
36
|
+
/** Live lookup of a provider's SSRF-validated upstream host. */
|
|
37
|
+
readonly byokUpstreamHosts?: { readonly get: (providerId: string) => ByokUpstreamHost | undefined };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Instruction prepended to the first image-carrying user turn. */
|
|
41
|
+
const VISION_HANDOFF_INSTRUCTION =
|
|
42
|
+
"Describe the contents of the image(s) attached to this message. Be factual, " +
|
|
43
|
+
"concise, and specific: mention visible text, people, objects, layout, colors, " +
|
|
44
|
+
"and anything else notable.";
|
|
45
|
+
|
|
46
|
+
/** Minimum remaining deadline before we attempt a handoff at all. */
|
|
47
|
+
const VISION_HANDOFF_MIN_BUDGET_MS = 2_000;
|
|
48
|
+
|
|
49
|
+
/** Per-model budget cap so one slow vision model cannot eat the whole deadline. */
|
|
50
|
+
const VISION_HANDOFF_PER_MODEL_TIMEOUT_MS = 12_000;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Fallback context window for a vision model whose catalog row is unknown.
|
|
54
|
+
* Used only to bound how much trailing history a handoff sub-request carries;
|
|
55
|
+
* absent that boundary a giant conversation could overflow the vision model.
|
|
56
|
+
*/
|
|
57
|
+
const VISION_HANDOFF_FALLBACK_CONTEXT_TOKENS = 200_000;
|
|
58
|
+
|
|
59
|
+
/** Headroom kept for the describe-instruction and the active image turn. */
|
|
60
|
+
const VISION_HANDOFF_CONTEXT_HEADROOM_TOKENS = 8_000;
|
|
61
|
+
|
|
62
|
+
/** The result of one successful handoff. */
|
|
63
|
+
export interface VisionHandoffResult {
|
|
64
|
+
readonly description: string;
|
|
65
|
+
readonly model: string;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Inputs for running the handoff across an ordered model list. */
|
|
69
|
+
|
|
70
|
+
/** The shape the preparer calls; matches `runVisionHandoff` in preparer deps. */
|
|
71
|
+
export interface VisionHandoffCall {
|
|
72
|
+
readonly canonicalRequest: CanonicalRequest;
|
|
73
|
+
readonly authorization: ResolvedApiKey;
|
|
74
|
+
readonly deadlineMs: number;
|
|
75
|
+
readonly signal?: AbortSignal;
|
|
76
|
+
readonly clientUserAgent?: string;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* True when a content part is an image or the legacy `[image]` placeholder
|
|
81
|
+
* that a degraded (non-image-capable) route substitutes for one.
|
|
82
|
+
*/
|
|
83
|
+
export function isImageOrPlaceholderPart(part: ContentPart): boolean {
|
|
84
|
+
return part.kind === "image" || (part.kind === "text" && part.text === "[image]");
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Index of the last user turn that carries images (or their `[image]`
|
|
89
|
+
* placeholders). The handoff describes only this turn; older image-carrying
|
|
90
|
+
* turns are history and are dropped by the caller's replacement pass (they
|
|
91
|
+
* are not sent to the vision model either).
|
|
92
|
+
*/
|
|
93
|
+
export function lastImageCarryingUserTurnIndex(request: CanonicalRequest): number {
|
|
94
|
+
for (let i = request.messages.length - 1; i >= 0; i -= 1) {
|
|
95
|
+
const message = request.messages[i]!;
|
|
96
|
+
if (message.role === "user" && message.content.some(isImageOrPlaceholderPart)) {
|
|
97
|
+
return i;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return -1;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** Index of the last user turn that carries a real image (not a placeholder). */
|
|
104
|
+
function lastRealImageCarryingUserTurnIndex(request: CanonicalRequest): number {
|
|
105
|
+
for (let i = request.messages.length - 1; i >= 0; i -= 1) {
|
|
106
|
+
const message = request.messages[i]!;
|
|
107
|
+
if (message.role === "user" && message.content.some((part) => part.kind === "image")) {
|
|
108
|
+
return i;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return -1;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Selects the messages sent to one vision model: the active image-carrying
|
|
116
|
+
* turn (with the describe-instruction) plus as much *trailing* history as fits
|
|
117
|
+
* the vision model's context window. Carrying only the last turn loses the
|
|
118
|
+
* surrounding conversation ("memory lost to handoff"), while carrying all of it
|
|
119
|
+
* overflows a small-context vision model. Walking backwards from the image turn
|
|
120
|
+
* and estimating tokens keeps the sub-request within the model's context without
|
|
121
|
+
* guessing.
|
|
122
|
+
*/
|
|
123
|
+
function selectVisionContextMessages(parent: CanonicalRequest): CanonicalMessage[] {
|
|
124
|
+
const index = lastRealImageCarryingUserTurnIndex(parent);
|
|
125
|
+
if (index < 0) {
|
|
126
|
+
throw new GatewayError(
|
|
127
|
+
"invalid_request",
|
|
128
|
+
400,
|
|
129
|
+
"vision handoff cannot build a sub-request from a request with no image-carrying user turns (request {requestId})",
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
const active = parent.messages[index]!;
|
|
133
|
+
const kept = active.content.filter((part) => part.kind === "text" || part.kind === "image");
|
|
134
|
+
const activeTurn: CanonicalMessage = {
|
|
135
|
+
role: "user",
|
|
136
|
+
content: [{ kind: "text", text: VISION_HANDOFF_INSTRUCTION }, ...kept],
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
const contextLimit = visionModelContextLimit(parent);
|
|
140
|
+
const budget = Math.max(1, contextLimit - VISION_HANDOFF_CONTEXT_HEADROOM_TOKENS);
|
|
141
|
+
const messages: CanonicalMessage[] = [activeTurn];
|
|
142
|
+
for (let i = index - 1; i >= 0; i -= 1) {
|
|
143
|
+
const message = parent.messages[i]!;
|
|
144
|
+
const candidateMessages = [message, ...messages];
|
|
145
|
+
const estimated = estimateInputTokens({ ...parent, messages: candidateMessages });
|
|
146
|
+
if (estimated > budget) break;
|
|
147
|
+
messages.length = 0;
|
|
148
|
+
messages.push(...candidateMessages);
|
|
149
|
+
}
|
|
150
|
+
return messages;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Resolves the vision model's context window. Prefers:
|
|
155
|
+
* 1. an explicit operator-provided limit on the request metadata;
|
|
156
|
+
* 2. the packed catalog row for the first vision model (the one the handoff
|
|
157
|
+
* will actually dispatch to);
|
|
158
|
+
* 3. the fallback window.
|
|
159
|
+
*/
|
|
160
|
+
function visionModelContextLimit(parent: CanonicalRequest): number {
|
|
161
|
+
const explicit = parent.metadata?.["visionModelContextLimit"];
|
|
162
|
+
if (typeof explicit === "number" && Number.isFinite(explicit) && explicit > 0) {
|
|
163
|
+
return explicit;
|
|
164
|
+
}
|
|
165
|
+
const row = modelsDevCatalog.resolve("", parent.model);
|
|
166
|
+
if (row !== undefined && row.contextLimit !== null && row.contextLimit > 0) {
|
|
167
|
+
return row.contextLimit;
|
|
168
|
+
}
|
|
169
|
+
return VISION_HANDOFF_FALLBACK_CONTEXT_TOKENS;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** Deterministic cache key: the exact context the vision model will see. */
|
|
173
|
+
function visionContextCacheKey(parent: CanonicalRequest): string {
|
|
174
|
+
const messages = selectVisionContextMessages(parent);
|
|
175
|
+
const serialized = JSON.stringify(messages);
|
|
176
|
+
return createHash("sha256").update(serialized).digest("hex");
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Builds the sub-request sent to one vision model.
|
|
181
|
+
*/
|
|
182
|
+
function buildVisionSubRequest(parent: CanonicalRequest, model: string): CanonicalRequest {
|
|
183
|
+
return {
|
|
184
|
+
model,
|
|
185
|
+
messages: selectVisionContextMessages(parent),
|
|
186
|
+
generation_controls: {},
|
|
187
|
+
stream: true,
|
|
188
|
+
source_surface: "chat",
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Tries the ordered vision-model list and returns every description produced
|
|
194
|
+
* (one per model that answered). Each model gets its own attempt-loop
|
|
195
|
+
* sub-dispatch with its own admission/pool slot (isolated from the parent
|
|
196
|
+
* request) and its own child request state. Any failure — an unroutable or
|
|
197
|
+
* unauthorized model, an upstream error, an empty reply — falls through to
|
|
198
|
+
* the next entry.
|
|
199
|
+
*/
|
|
200
|
+
export async function tryVisionHandoff(
|
|
201
|
+
deps: VisionHandoffDeps,
|
|
202
|
+
input: VisionHandoffCall,
|
|
203
|
+
models: readonly string[],
|
|
204
|
+
): Promise<readonly VisionHandoffResult[]> {
|
|
205
|
+
// The cache key covers the full vision context (active image turn plus the
|
|
206
|
+
// trailing history we actually send), so a changed conversation re-describes
|
|
207
|
+
// the same attachment instead of serving a stale description. A malformed
|
|
208
|
+
// request that cannot build a vision context simply skips the cache.
|
|
209
|
+
let cacheHash = "";
|
|
210
|
+
try {
|
|
211
|
+
cacheHash = visionContextCacheKey(input.canonicalRequest);
|
|
212
|
+
} catch {
|
|
213
|
+
cacheHash = "";
|
|
214
|
+
}
|
|
215
|
+
const cached = cacheHash.length > 0 ? getCachedVisionDescription(cacheHash) : undefined;
|
|
216
|
+
if (cached !== undefined && cached.trim().length > 0) {
|
|
217
|
+
return [{ description: cached.trim(), model: models[0] ?? "cache" }];
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const results: readonly VisionHandoffResult[] = await trialModelsInParallel(deps, input, models);
|
|
221
|
+
if (cacheHash.length > 0 && results.length > 0) {
|
|
222
|
+
setCachedVisionDescription(cacheHash, results[0]!.description);
|
|
223
|
+
}
|
|
224
|
+
return results;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/** Tries the ordered vision-model list, in parallel, with a per-model budget. */
|
|
228
|
+
async function trialModelsInParallel(
|
|
229
|
+
deps: VisionHandoffDeps,
|
|
230
|
+
input: VisionHandoffCall,
|
|
231
|
+
models: readonly string[],
|
|
232
|
+
): Promise<readonly VisionHandoffResult[]> {
|
|
233
|
+
if (models.length === 0) return [];
|
|
234
|
+
const remaining = input.deadlineMs - Date.now();
|
|
235
|
+
if (remaining <= VISION_HANDOFF_MIN_BUDGET_MS) return [];
|
|
236
|
+
|
|
237
|
+
const perModelBudget = Math.min(
|
|
238
|
+
remaining,
|
|
239
|
+
VISION_HANDOFF_PER_MODEL_TIMEOUT_MS,
|
|
240
|
+
);
|
|
241
|
+
const decided = await Promise.all(
|
|
242
|
+
models.map((model) => trialOneModel(deps, input, model, perModelBudget)),
|
|
243
|
+
);
|
|
244
|
+
return decided.filter(
|
|
245
|
+
(result): result is VisionHandoffResult => result !== null,
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/** Runs one vision-model trial with its own attempt-loop. Null on any failure. */
|
|
250
|
+
async function trialOneModel(
|
|
251
|
+
deps: VisionHandoffDeps,
|
|
252
|
+
input: VisionHandoffCall,
|
|
253
|
+
model: string,
|
|
254
|
+
budgetMs: number,
|
|
255
|
+
): Promise<VisionHandoffResult | null> {
|
|
256
|
+
const childRequest = new Request("http://cartethyia.invalid/vision-handoff", {
|
|
257
|
+
method: "POST",
|
|
258
|
+
});
|
|
259
|
+
const childState = deps.stateStore.initialize(childRequest, Date.now(), budgetMs);
|
|
260
|
+
childState.authorization = input.authorization;
|
|
261
|
+
childState.ingressBody = null;
|
|
262
|
+
try {
|
|
263
|
+
let prepared: PreparedProxyRequest;
|
|
264
|
+
try {
|
|
265
|
+
prepared = await deps.proxyPreparer.prepare({
|
|
266
|
+
canonicalRequest: buildVisionSubRequest(input.canonicalRequest, model),
|
|
267
|
+
authorization: input.authorization,
|
|
268
|
+
deadlineMs: budgetMs,
|
|
269
|
+
// The sub-request itself must never trigger a nested handoff.
|
|
270
|
+
disableVisionHandoff: true,
|
|
271
|
+
...(input.signal === undefined ? {} : { signal: input.signal }),
|
|
272
|
+
...(input.clientUserAgent === undefined ? {} : { clientUserAgent: input.clientUserAgent }),
|
|
273
|
+
});
|
|
274
|
+
} catch (error) {
|
|
275
|
+
// Unroutable / not allowed for this key / no account — try the next.
|
|
276
|
+
log.warn("[vision-handoff] vision model skipped", {
|
|
277
|
+
model,
|
|
278
|
+
tenantId: input.authorization.tenantId,
|
|
279
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
280
|
+
});
|
|
281
|
+
return null;
|
|
282
|
+
}
|
|
283
|
+
if (prepared.candidate.capability_profile.image !== true) return null;
|
|
284
|
+
childState.canonicalRequest = prepared.canonicalRequest;
|
|
285
|
+
childState.preparedRequest = prepared;
|
|
286
|
+
|
|
287
|
+
const text = await runAttemptLoop<string, ProviderAdapter>({
|
|
288
|
+
state: childState,
|
|
289
|
+
deps,
|
|
290
|
+
leaseSource: {
|
|
291
|
+
admissionService: prepared.admissionService,
|
|
292
|
+
routingEngine: prepared.routingEngine,
|
|
293
|
+
plan: prepared.plan,
|
|
294
|
+
estimatedInputTokens: prepared.estimatedInputTokens,
|
|
295
|
+
estimatedOutputTokens: prepared.estimatedOutputTokens,
|
|
296
|
+
authorizationSnapshot: prepared.authorization.snapshot,
|
|
297
|
+
},
|
|
298
|
+
candidates:
|
|
299
|
+
prepared.eligibleRouteCandidates.length > 0
|
|
300
|
+
? prepared.eligibleRouteCandidates
|
|
301
|
+
: [prepared.candidate],
|
|
302
|
+
tenantId: prepared.authorization.tenantId,
|
|
303
|
+
strictPoolSelection: true,
|
|
304
|
+
resolveHost: (candidate) => deps.byokUpstreamHosts?.get(candidate.provider_id),
|
|
305
|
+
exhaustedError: new GatewayError(
|
|
306
|
+
"route_unavailable",
|
|
307
|
+
503,
|
|
308
|
+
"no eligible vision route (request {requestId}). The bouncer stepped out — come back shortly 🚪",
|
|
309
|
+
),
|
|
310
|
+
prepare: async (candidate) => {
|
|
311
|
+
if (!candidate.provider_account_id && candidate.requires_account !== false) {
|
|
312
|
+
throw new GatewayError(
|
|
313
|
+
"route_unavailable",
|
|
314
|
+
503,
|
|
315
|
+
"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 🔑",
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
const credential = candidate.provider_account_id
|
|
319
|
+
? await resolveCredentialForAccount(
|
|
320
|
+
deps.db,
|
|
321
|
+
candidate.provider_id,
|
|
322
|
+
candidate.provider_account_id,
|
|
323
|
+
deps.oauthRefreshService && deps.resolveOAuthRefresher
|
|
324
|
+
? { refreshService: deps.oauthRefreshService, resolveRefresher: deps.resolveOAuthRefresher }
|
|
325
|
+
: undefined,
|
|
326
|
+
)
|
|
327
|
+
: {
|
|
328
|
+
provider_id: candidate.provider_id as ProviderAdapter["provider_id"],
|
|
329
|
+
credential_kind: "none" as const,
|
|
330
|
+
};
|
|
331
|
+
const adapter = await deps.resolveProviderAdapter?.(candidate.provider_id);
|
|
332
|
+
if (!adapter) {
|
|
333
|
+
throw new GatewayError(
|
|
334
|
+
"route_unavailable",
|
|
335
|
+
503,
|
|
336
|
+
"provider adapter not registered (request {requestId}). Even we have bad days — check the registry wiring 🐻",
|
|
337
|
+
);
|
|
338
|
+
}
|
|
339
|
+
return { credential, adapter };
|
|
340
|
+
},
|
|
341
|
+
attempt: async (context) => {
|
|
342
|
+
const { candidate, credential, adapter, leases } = context;
|
|
343
|
+
const candidateRequest =
|
|
344
|
+
candidate.model_id === prepared.canonicalRequest.model
|
|
345
|
+
? prepared.canonicalRequest
|
|
346
|
+
: { ...prepared.canonicalRequest, model: candidate.model_id };
|
|
347
|
+
const attemptRequest = projectForRoute(candidateRequest, routeCapabilitiesFor(candidate));
|
|
348
|
+
const providerRouteCandidate = {
|
|
349
|
+
provider_id: candidate.provider_id,
|
|
350
|
+
model_id: candidate.model_id,
|
|
351
|
+
wire_family: candidate.wire_family,
|
|
352
|
+
endpoint_path: candidate.endpoint,
|
|
353
|
+
...(candidate.output_limit == null ? {} : { output_limit: candidate.output_limit }),
|
|
354
|
+
capabilities: candidate.capability_profile,
|
|
355
|
+
user_agent: candidate.user_agent,
|
|
356
|
+
...(candidate.dialect === undefined ? {} : { dialect: candidate.dialect }),
|
|
357
|
+
};
|
|
358
|
+
const dispatchContextBase = buildUpstreamDispatchContext({
|
|
359
|
+
credential,
|
|
360
|
+
deadline: childState.deadlineMs,
|
|
361
|
+
signal: childState.abortController.signal,
|
|
362
|
+
headers: {},
|
|
363
|
+
requestId: childState.requestId,
|
|
364
|
+
...(candidate.user_agent === undefined ? {} : { userAgent: candidate.user_agent }),
|
|
365
|
+
...(deps.networkBindingFactory
|
|
366
|
+
? {
|
|
367
|
+
outboundFetch: deps.networkBindingFactory.fetch(
|
|
368
|
+
leases.proxySlot?.poolId ?? "",
|
|
369
|
+
prepared.authorization.tenantId,
|
|
370
|
+
),
|
|
371
|
+
}
|
|
372
|
+
: {}),
|
|
373
|
+
});
|
|
374
|
+
const events: CanonicalEvent[] = [];
|
|
375
|
+
for await (const event of adapter.dispatch(
|
|
376
|
+
attemptRequest,
|
|
377
|
+
providerRouteCandidate as never,
|
|
378
|
+
dispatchContextBase as never,
|
|
379
|
+
)) {
|
|
380
|
+
events.push(event);
|
|
381
|
+
}
|
|
382
|
+
const terminal = events.find((event) => event.type === "terminal");
|
|
383
|
+
if (terminal === undefined) throw terminalFailure(undefined);
|
|
384
|
+
const terminalError = terminalFailure(terminal);
|
|
385
|
+
if (terminalError !== undefined) throw terminalError;
|
|
386
|
+
let text = "";
|
|
387
|
+
for (const event of events) {
|
|
388
|
+
if (event.type === "content_delta" && event.content.kind === "text") {
|
|
389
|
+
text += event.content.text;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
const usage =
|
|
393
|
+
terminal.usage ??
|
|
394
|
+
estimatedUsage(prepared.estimatedInputTokens, prepared.estimatedOutputTokens);
|
|
395
|
+
const pricedUsage = repriceUsage(usage, candidate.provider_id, candidate.model_id);
|
|
396
|
+
await completeAttempt(childState, {
|
|
397
|
+
status: "completed",
|
|
398
|
+
...completionContext({
|
|
399
|
+
providerId: candidate.provider_id,
|
|
400
|
+
modelId: candidate.model_id,
|
|
401
|
+
tenantId: prepared.authorization.tenantId,
|
|
402
|
+
ingressBody: null,
|
|
403
|
+
db: deps.db,
|
|
404
|
+
...(candidate.provider_account_id ? { accountId: candidate.provider_account_id } : {}),
|
|
405
|
+
...(candidate.provider_account_label ? { accountLabel: candidate.provider_account_label } : {}),
|
|
406
|
+
...(leases.proxySlot ? { networkPoolId: leases.proxySlot.poolId } : {}),
|
|
407
|
+
...(leases.lease ? { lease: leases.lease } : {}),
|
|
408
|
+
...(deps.telemetryBuffer ? { telemetryBuffer: deps.telemetryBuffer } : {}),
|
|
409
|
+
...(deps.snapshotService ? { snapshotService: deps.snapshotService } : {}),
|
|
410
|
+
}),
|
|
411
|
+
usage: pricedUsage,
|
|
412
|
+
commitUsage: pricedUsage,
|
|
413
|
+
responseBody: null,
|
|
414
|
+
clientResponseText: text,
|
|
415
|
+
});
|
|
416
|
+
return text;
|
|
417
|
+
},
|
|
418
|
+
});
|
|
419
|
+
if (text.trim().length === 0) return null;
|
|
420
|
+
return { description: text.trim(), model };
|
|
421
|
+
} catch (error) {
|
|
422
|
+
log.warn("[vision-handoff] vision model attempt failed", {
|
|
423
|
+
model,
|
|
424
|
+
tenantId: input.authorization.tenantId,
|
|
425
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
426
|
+
});
|
|
427
|
+
return null;
|
|
428
|
+
} finally {
|
|
429
|
+
childState.cleanup();
|
|
430
|
+
}
|
|
431
|
+
}
|
|
@@ -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
|