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
|
@@ -66,13 +66,13 @@ const MAX_FILES_PER_RUN = 64;
|
|
|
66
66
|
*/
|
|
67
67
|
const HOURS_TO_SKIP = 2;
|
|
68
68
|
|
|
69
|
-
|
|
69
|
+
interface ReconcileOptions {
|
|
70
70
|
/** Report only: count the gap without writing anything. */
|
|
71
71
|
readonly verify?: boolean;
|
|
72
72
|
readonly now?: Date;
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
|
|
75
|
+
interface ReconcileReport {
|
|
76
76
|
readonly ran: boolean;
|
|
77
77
|
/** Why the run did nothing, when it did nothing. */
|
|
78
78
|
readonly skippedReason?: "lease_held" | "no_files" | "uptodate";
|
|
@@ -393,7 +393,7 @@ async function insertChunk(
|
|
|
393
393
|
.set({
|
|
394
394
|
fileName,
|
|
395
395
|
byteOffset,
|
|
396
|
-
filesCompleted: sql`${telemetryReconciliationState.filesCompleted} +
|
|
396
|
+
filesCompleted: sql`${telemetryReconciliationState.filesCompleted} + 1`,
|
|
397
397
|
eventsExamined: sql`${telemetryReconciliationState.eventsExamined} + ${events.length}`,
|
|
398
398
|
eventsInserted: sql`${telemetryReconciliationState.eventsInserted} + ${rows.length}`,
|
|
399
399
|
conflicts: sql`${telemetryReconciliationState.conflicts} + ${events.length - rows.length}`,
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
* but those spans measure local response processing, not decode. Only a
|
|
32
32
|
* client-visible stream window counts as decode time.
|
|
33
33
|
*/
|
|
34
|
-
|
|
34
|
+
interface TokenSpeedInput {
|
|
35
35
|
/** Completion/output tokens reported for the request. */
|
|
36
36
|
readonly outputTokens?: number | undefined;
|
|
37
37
|
/** End-to-end gateway latency in milliseconds. */
|
|
@@ -23,7 +23,7 @@ export interface ConnectionUrlCandidate {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
/** How one backing service's connection string is resolved. */
|
|
26
|
-
|
|
26
|
+
interface ConnectionUrlSpec {
|
|
27
27
|
/** Accepted URL schemes, e.g. `postgres:` / `postgresql:`. */
|
|
28
28
|
readonly schemes: readonly string[];
|
|
29
29
|
/** URL variables, in resolution order. */
|
|
@@ -17,7 +17,7 @@ export const fullSchema = schema;
|
|
|
17
17
|
export type CartethyiaDatabase = NodePgDatabase<typeof fullSchema>;
|
|
18
18
|
|
|
19
19
|
/** Raw rows from a parameter query, without driver-specific decoration. */
|
|
20
|
-
|
|
20
|
+
interface QueryRows {
|
|
21
21
|
readonly rows: unknown[];
|
|
22
22
|
}
|
|
23
23
|
|
package/src/persistence/db-pg.ts
CHANGED
|
@@ -81,7 +81,7 @@ function databaseUrlFromLibpqEnv(): string | undefined {
|
|
|
81
81
|
return `postgres://${credentials}${authorityHost}:${port}${path}`;
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
|
|
84
|
+
function requireDatabaseUrl(): string {
|
|
85
85
|
return requireConnectionUrl({
|
|
86
86
|
schemes: ["postgres:", "postgresql:"],
|
|
87
87
|
candidates: databaseUrlCandidates(),
|
|
@@ -124,7 +124,7 @@ export function poolIdleFloorFromEnv(): number {
|
|
|
124
124
|
* only closes an idle client after `idleTimeoutMillis` (60s here); this gives
|
|
125
125
|
* the pool a faster, explicit shrink path while still keeping the floor warm.
|
|
126
126
|
*/
|
|
127
|
-
|
|
127
|
+
function startPoolShrinker(pool: Pool, floor: number): () => void {
|
|
128
128
|
const interval = setInterval(() => {
|
|
129
129
|
try {
|
|
130
130
|
const idle = pool.idleCount;
|
|
@@ -326,7 +326,7 @@ const AUTH_KEY_COLUMNS = [
|
|
|
326
326
|
"id", "tenant_id", "key_hash", "key_mode", "parent_key_id", "issued_client_ip",
|
|
327
327
|
"issued_client_ip_key", "label", "scopes", "requests_per_minute", "daily_token_limit",
|
|
328
328
|
"monthly_token_limit", "lifetime_token_budget", "lifetime_tokens_consumed",
|
|
329
|
-
"max_concurrent_requests", "model_access_mode", "model_list", "client_router_denylist",
|
|
329
|
+
"max_concurrent_requests", "concurrency_scope", "model_access_mode", "model_list", "client_router_denylist",
|
|
330
330
|
"model_prefix", "created_at", "enabled", "revoked_at", "banned_at", "ban_reason", "banned_until",
|
|
331
331
|
"key_prefix", "key_encrypted",
|
|
332
332
|
"notes_title", "notes_subtitle", "notes_body", "is_default", "sort_index",
|
|
@@ -341,7 +341,7 @@ export interface SqliteAuthStatements {
|
|
|
341
341
|
readonly childrenConsumed: { get: (parentId: string) => { total: number } | null };
|
|
342
342
|
}
|
|
343
343
|
|
|
344
|
-
|
|
344
|
+
function prepareAuthStatements(sqlite: Database): SqliteAuthStatements {
|
|
345
345
|
// A key is active while not revoked and not under an active ban: a ban is
|
|
346
346
|
// active when `banned_at` is set and `banned_until` is null (permanent) or
|
|
347
347
|
// still in the future. SQLite stores timestamps as ISO text, so the
|
|
@@ -507,7 +507,7 @@ function applyFilePragmas(db: Database): void {
|
|
|
507
507
|
}
|
|
508
508
|
|
|
509
509
|
/** The SQLite set lives beside the PostgreSQL one, never inside it. */
|
|
510
|
-
|
|
510
|
+
function resolveSqliteMigrationsFolder(): string {
|
|
511
511
|
return resolvePackagePath("migrations", "lite-sqlite3");
|
|
512
512
|
}
|
|
513
513
|
|
|
@@ -143,7 +143,11 @@ export function affectedRows(result: unknown): number {
|
|
|
143
143
|
* `.for()`, but a transaction there is already the write lock, so the query
|
|
144
144
|
* runs as-is. The two builders disagree on `.for()`, so the call is typed at
|
|
145
145
|
* the boundary instead of pushed into every store.
|
|
146
|
+
*
|
|
147
|
+
* The dialect is read from the query itself, not the process environment: a
|
|
148
|
+
* test may build a SQLite handle while the suite runs in full mode, and a
|
|
149
|
+
* PostgreSQL query always carries `.for` while a SQLite one never does.
|
|
146
150
|
*/
|
|
147
151
|
export function lockRows<T>(query: T & { for?(mode: string): PromiseLike<unknown> }): PromiseLike<T extends PromiseLike<infer R> ? R : never> {
|
|
148
|
-
return (
|
|
152
|
+
return (typeof query.for === "function" ? query.for!("update") : query) as PromiseLike<T extends PromiseLike<infer R> ? R : never>;
|
|
149
153
|
}
|
|
@@ -45,7 +45,7 @@ function sameMemoKey(left: ReadinessMemoKey, right: ReadinessMemoKey): boolean {
|
|
|
45
45
|
* Returns typed ready/not_ready response.
|
|
46
46
|
*/
|
|
47
47
|
|
|
48
|
-
|
|
48
|
+
type ReadinessStatus = "ready" | "not_ready";
|
|
49
49
|
|
|
50
50
|
export interface ReadinessCheckResult {
|
|
51
51
|
readonly status: ReadinessStatus;
|
package/src/persistence/redis.ts
CHANGED
|
@@ -92,7 +92,7 @@ export function resolveRedisBackend(client: RedisClient | undefined): RedisBacke
|
|
|
92
92
|
return client === undefined ? "memory" : "redis";
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
-
|
|
95
|
+
interface CloseRedisOptions {
|
|
96
96
|
/** Bounded wait for `QUIT` before falling back to `disconnect()`. Defaults to 2s. */
|
|
97
97
|
readonly quitTimeoutMs?: number;
|
|
98
98
|
}
|
|
@@ -4,9 +4,23 @@ import {
|
|
|
4
4
|
bigint, boolean, check, customType, index, integer, jsonb, numeric, pgEnum, pgTable,
|
|
5
5
|
primaryKey, text, timestamp, unique, uniqueIndex, uuid, type AnyPgColumn,
|
|
6
6
|
} from "drizzle-orm/pg-core";
|
|
7
|
+
import type { ProviderQuotaResult } from "../providers/quota/quota-contracts";
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
|
|
9
|
+
/**
|
|
10
|
+
* The persisted copy of one account's last successful quota read.
|
|
11
|
+
*
|
|
12
|
+
* Survives process restarts, which the in-process quota cache does not — so a
|
|
13
|
+
* cold page open can render the last known balance immediately while the sweep
|
|
14
|
+
* refreshes it. Stored only on success; a failed fetch leaves the previous
|
|
15
|
+
* snapshot in place.
|
|
16
|
+
*/
|
|
17
|
+
export interface ProviderQuotaResultSnapshot {
|
|
18
|
+
readonly quota: ProviderQuotaResult;
|
|
19
|
+
readonly fetchedAt: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const TENANT_STATUSES = ["active", "suspended", "deleted"] as const;
|
|
23
|
+
type TenantStatus = (typeof TENANT_STATUSES)[number];
|
|
10
24
|
|
|
11
25
|
export const tenants = pgTable(
|
|
12
26
|
"tenants",
|
|
@@ -26,35 +40,35 @@ export const tenants = pgTable(
|
|
|
26
40
|
|
|
27
41
|
// Canonical tenant/timestamp column definitions shared by every table.
|
|
28
42
|
|
|
29
|
-
|
|
43
|
+
function tenantRefNullable() {
|
|
30
44
|
return uuid("tenant_id").references(() => tenants.id, { onDelete: "cascade" });
|
|
31
45
|
}
|
|
32
46
|
|
|
33
|
-
|
|
47
|
+
function tenantRefRequired() {
|
|
34
48
|
return uuid("tenant_id")
|
|
35
49
|
.notNull()
|
|
36
50
|
.references(() => tenants.id, { onDelete: "cascade" });
|
|
37
51
|
}
|
|
38
52
|
|
|
39
|
-
|
|
53
|
+
function tenantRefPrimaryKey() {
|
|
40
54
|
return uuid("tenant_id")
|
|
41
55
|
.primaryKey()
|
|
42
56
|
.references(() => tenants.id, { onDelete: "cascade" });
|
|
43
57
|
}
|
|
44
58
|
|
|
45
|
-
|
|
59
|
+
function tenantCascadeSetNull() {
|
|
46
60
|
return uuid("tenant_id").references(() => tenants.id, { onDelete: "set null" });
|
|
47
61
|
}
|
|
48
62
|
|
|
49
|
-
|
|
63
|
+
function createdAtColumn() {
|
|
50
64
|
return timestamp("created_at", { withTimezone: true }).notNull().defaultNow();
|
|
51
65
|
}
|
|
52
66
|
|
|
53
|
-
|
|
67
|
+
function updatedAtColumn() {
|
|
54
68
|
return timestamp("updated_at", { withTimezone: true }).notNull().defaultNow();
|
|
55
69
|
}
|
|
56
70
|
|
|
57
|
-
|
|
71
|
+
function timestampColumns() {
|
|
58
72
|
return { createdAt: createdAtColumn(), updatedAt: updatedAtColumn() };
|
|
59
73
|
}
|
|
60
74
|
|
|
@@ -90,7 +104,7 @@ export const healthStatus = pgEnum("health_status", ["active", "cooldown", "disa
|
|
|
90
104
|
* One definition, because the two tables are driven by the same transitions and
|
|
91
105
|
* a column added to one of them by hand is how they drift.
|
|
92
106
|
*/
|
|
93
|
-
|
|
107
|
+
function healthColumns() {
|
|
94
108
|
return {
|
|
95
109
|
status: healthStatus("status").notNull().default("active"),
|
|
96
110
|
consecutiveFailures: integer("consecutive_failures").notNull().default(0),
|
|
@@ -295,6 +309,18 @@ export const providerAccounts = pgTable("provider_accounts", {
|
|
|
295
309
|
* to an account that may well be refused.
|
|
296
310
|
*/
|
|
297
311
|
quotaPlan: text("quota_plan"),
|
|
312
|
+
/**
|
|
313
|
+
* The last successful quota read for this account, as a JSON snapshot.
|
|
314
|
+
*
|
|
315
|
+
* Persisted rather than left only in the quota cache: that cache is a short
|
|
316
|
+
* in-process TTL and empty on a cold start, so right after a restart the
|
|
317
|
+
* console would show no balance for every account until the sweep re-read
|
|
318
|
+
* them. The row survives the restart, so a cold page open renders the last
|
|
319
|
+
* known numbers immediately (and the sweep refreshes them). Only a
|
|
320
|
+
* *successful* read is stored — a failed fetch leaves the previous snapshot
|
|
321
|
+
* in place, mirroring the plan column's behaviour.
|
|
322
|
+
*/
|
|
323
|
+
quotaSnapshot: jsonb("quota_snapshot").$type<ProviderQuotaResultSnapshot | null>(),
|
|
298
324
|
|
|
299
325
|
/**
|
|
300
326
|
* Qoder native machine identity bound to this account, as one JSON object
|
|
@@ -342,7 +368,6 @@ export const providerAccounts = pgTable("provider_accounts", {
|
|
|
342
368
|
],
|
|
343
369
|
);
|
|
344
370
|
|
|
345
|
-
|
|
346
371
|
// OAuth refresh token + cross-process lease, normalized out of
|
|
347
372
|
// `provider_accounts` into its own 1:0/1 table. The vast majority of
|
|
348
373
|
// provider accounts are `credentialKind: "api_key"`, for which these four
|
|
@@ -381,7 +406,6 @@ export const providerOauthStates = pgTable("provider_oauth_states", {
|
|
|
381
406
|
leaseExpiresAt: timestamp("lease_expires_at", { withTimezone: true }),
|
|
382
407
|
});
|
|
383
408
|
|
|
384
|
-
|
|
385
409
|
// One row per (provider_id, model_id, route), with `endpoint_path`
|
|
386
410
|
// identifying the route — the same model through two providers, or through
|
|
387
411
|
// two routes of one provider, is two rows. `enabled` is a hard routing
|
|
@@ -423,7 +447,7 @@ export const models = pgTable(
|
|
|
423
447
|
* that predicts access — `credits` and `vendor` were both measured against
|
|
424
448
|
* real dispatches and neither correlates. Only the refusal itself is a
|
|
425
449
|
* signal, so the answer has to be recorded. See
|
|
426
|
-
*
|
|
450
|
+
* `documentation/research/codebuddy/2026-10-19-codebuddy-chat-dispatch-headers.md`.
|
|
427
451
|
*/
|
|
428
452
|
proOnly: boolean("pro_only"),
|
|
429
453
|
/**
|
|
@@ -435,6 +459,12 @@ export const models = pgTable(
|
|
|
435
459
|
* provider did not state it, which is not the same as billed.
|
|
436
460
|
*/
|
|
437
461
|
freeTier: boolean("free_tier"),
|
|
462
|
+
/**
|
|
463
|
+
* Explicit wire dialect for this row (nullable). Absent = resolver
|
|
464
|
+
* fallback (heuristic + family default). See
|
|
465
|
+
* `src/protocol/translation/dialect/`.
|
|
466
|
+
*/
|
|
467
|
+
dialect: text("dialect"),
|
|
438
468
|
},
|
|
439
469
|
(table) => [
|
|
440
470
|
uniqueIndex("models_provider_model_route_uidx").on(
|
|
@@ -476,7 +506,6 @@ export const tenantDisabledModels = pgTable(
|
|
|
476
506
|
],
|
|
477
507
|
);
|
|
478
508
|
|
|
479
|
-
|
|
480
509
|
// Tenant-owned HTTP/HTTPS/SOCKS5 pools share the provider-account health state machine.
|
|
481
510
|
export const networkPools = pgTable("network_pools", {
|
|
482
511
|
id: uuid("id").primaryKey().defaultRandom(),
|
|
@@ -611,8 +640,6 @@ export const providerPoolAccounts = pgTable(
|
|
|
611
640
|
index("provider_pool_accounts_provider_status_idx").on(table.providerId, table.status),
|
|
612
641
|
],
|
|
613
642
|
);
|
|
614
|
-
export type ProviderPoolAccount = typeof providerPoolAccounts.$inferSelect;
|
|
615
|
-
|
|
616
643
|
|
|
617
644
|
// Account and network-pool health events share one table. The entity kind
|
|
618
645
|
// selects exactly one owner FK, and the CHECK constraint below preserves
|
|
@@ -653,7 +680,6 @@ export const healthEvents = pgTable(
|
|
|
653
680
|
],
|
|
654
681
|
);
|
|
655
682
|
|
|
656
|
-
|
|
657
683
|
// Model aliasing & combos. Aliases resolve a client-facing name to a
|
|
658
684
|
// real model; combos back a client-facing name with >=1 real models,
|
|
659
685
|
// auto-selected via `fallback` or `round_robin`.
|
|
@@ -806,7 +832,6 @@ export const poolRoutingSettings = pgTable("pool_routing_settings", {
|
|
|
806
832
|
rotateCount: integer("rotate_count").notNull().default(1),
|
|
807
833
|
});
|
|
808
834
|
|
|
809
|
-
|
|
810
835
|
export const API_KEY_MODES = ["personal", "share"] as const;
|
|
811
836
|
export type ApiKeyMode = (typeof API_KEY_MODES)[number];
|
|
812
837
|
|
|
@@ -820,6 +845,10 @@ export type ApiKeyMode = (typeof API_KEY_MODES)[number];
|
|
|
820
845
|
export const API_KEY_MODEL_ACCESS_MODES = ["whitelist", "blacklist"] as const;
|
|
821
846
|
export type ApiKeyModelAccessMode = (typeof API_KEY_MODEL_ACCESS_MODES)[number];
|
|
822
847
|
|
|
848
|
+
/** Share-template concurrency scope — see `apiKeys.concurrencyScope`. */
|
|
849
|
+
export const API_KEY_CONCURRENCY_SCOPES = ["family", "per_key"] as const;
|
|
850
|
+
export type ApiKeyConcurrencyScope = (typeof API_KEY_CONCURRENCY_SCOPES)[number];
|
|
851
|
+
|
|
823
852
|
/**
|
|
824
853
|
* Inbound `/v1/*` keys never store plaintext secrets. Personal keys carry an
|
|
825
854
|
* authentication hash; share templates carry policy and child keys point back
|
|
@@ -858,6 +887,24 @@ export const apiKeys = pgTable(
|
|
|
858
887
|
.notNull()
|
|
859
888
|
.default(0),
|
|
860
889
|
maxConcurrentRequests: integer("max_concurrent_requests"),
|
|
890
|
+
/**
|
|
891
|
+
* How `maxConcurrentRequests` applies to a share family:
|
|
892
|
+
*
|
|
893
|
+
* - `family` (default) — one cap across the whole family: every recipient
|
|
894
|
+
* key shares the same admission counter and budget, so 20 means 20
|
|
895
|
+
* concurrent requests across all children of the template.
|
|
896
|
+
* - `per_key` — each recipient key gets its own independent concurrent
|
|
897
|
+
* window and cap. The cap is copied from the template to each child at
|
|
898
|
+
* issue time (children carry their own `maxConcurrentRequests`), and a
|
|
899
|
+
* child's admission identity is its own id, not the parent's.
|
|
900
|
+
*
|
|
901
|
+
* Only meaningful for `share` keys; personal keys always use their own
|
|
902
|
+
* per-key cap.
|
|
903
|
+
*/
|
|
904
|
+
concurrencyScope: text("concurrency_scope")
|
|
905
|
+
.$type<ApiKeyConcurrencyScope>()
|
|
906
|
+
.notNull()
|
|
907
|
+
.default("family"),
|
|
861
908
|
/**
|
|
862
909
|
* Slot cap for a share template: the maximum number of concurrently
|
|
863
910
|
* active child keys it may mint. `null` = unlimited (legacy behavior).
|
|
@@ -984,7 +1031,6 @@ export const sharePopups = pgTable(
|
|
|
984
1031
|
],
|
|
985
1032
|
);
|
|
986
1033
|
|
|
987
|
-
|
|
988
1034
|
// Public bearer links for enrolling one shared key per resolved client IP, and
|
|
989
1035
|
// for handing a personal key to its owner. The hash is the lookup key; the
|
|
990
1036
|
// token is additionally retained encrypted so the owner's console can show the
|
|
@@ -993,12 +1039,11 @@ export const sharePopups = pgTable(
|
|
|
993
1039
|
* Share-link kinds, as a runtime tuple. Route schemas and DTOs project this
|
|
994
1040
|
* list rather than restating it.
|
|
995
1041
|
*/
|
|
996
|
-
|
|
1042
|
+
const SHARE_LINK_KINDS = ["enroll", "handoff"] as const;
|
|
997
1043
|
|
|
998
1044
|
/** One share-link kind. */
|
|
999
1045
|
export type ShareLinkKind = (typeof SHARE_LINK_KINDS)[number];
|
|
1000
1046
|
|
|
1001
|
-
|
|
1002
1047
|
export const shareLinks = pgTable(
|
|
1003
1048
|
"share_links",
|
|
1004
1049
|
{
|
|
@@ -1040,7 +1085,6 @@ export const shareLinks = pgTable(
|
|
|
1040
1085
|
],
|
|
1041
1086
|
);
|
|
1042
1087
|
|
|
1043
|
-
|
|
1044
1088
|
/**
|
|
1045
1089
|
* Console dashboard users — distinct from API-key tenants.
|
|
1046
1090
|
*/
|
|
@@ -1113,7 +1157,6 @@ export const consoleLockouts = pgTable(
|
|
|
1113
1157
|
],
|
|
1114
1158
|
);
|
|
1115
1159
|
|
|
1116
|
-
|
|
1117
1160
|
// Configuration-change audit has its own retention lifecycle; it is separate
|
|
1118
1161
|
// from metadata telemetry and short-lived optional payload capture.
|
|
1119
1162
|
export const adminAuditLog = pgTable(
|
|
@@ -1136,7 +1179,6 @@ export const adminAuditLog = pgTable(
|
|
|
1136
1179
|
],
|
|
1137
1180
|
);
|
|
1138
1181
|
|
|
1139
|
-
|
|
1140
1182
|
// Runtime preferences are stored in one validated JSONB bag. The filter
|
|
1141
1183
|
// master toggle remains a typed column because it is read on the proxy path.
|
|
1142
1184
|
export interface ConsoleSettingsPreferences {
|
|
@@ -1232,6 +1274,14 @@ export interface ConsoleSettingsPreferences {
|
|
|
1232
1274
|
* the safer direction and the more common need.
|
|
1233
1275
|
*/
|
|
1234
1276
|
imageClampMaxBytes?: number | null;
|
|
1277
|
+
/**
|
|
1278
|
+
* Ordered list of vision model ids (`provider/model`) used to describe
|
|
1279
|
+
* images when the selected model is not image-capable. The first healthy
|
|
1280
|
+
* model wins; if it fails (cooldown/quota/upstream error), the handoff
|
|
1281
|
+
* falls through to the next entry. Empty/absent = legacy `[image]`
|
|
1282
|
+
* placeholder behavior.
|
|
1283
|
+
*/
|
|
1284
|
+
visionHandoffModels?: readonly string[];
|
|
1235
1285
|
}
|
|
1236
1286
|
|
|
1237
1287
|
export const consoleSettings = pgTable("console_settings", {
|
|
@@ -1242,21 +1292,33 @@ export const consoleSettings = pgTable("console_settings", {
|
|
|
1242
1292
|
|
|
1243
1293
|
// Tenant-scoped system prompt injection (append/override). One row per
|
|
1244
1294
|
// tenant; `global` NULL means never configured, distinct from an explicit
|
|
1245
|
-
// disabled setting. Providers/models are JSON object maps
|
|
1246
|
-
// providerId
|
|
1247
|
-
// `src/console/system-prompt/contracts.ts` for the value
|
|
1248
|
-
// resolution rules implemented by the dispatch-path transform.
|
|
1295
|
+
// disabled setting. Providers/models/combos/aliases are JSON object maps
|
|
1296
|
+
// keyed by providerId, `providerId::modelId`, combo name, and alias name
|
|
1297
|
+
// respectively — see `src/console/system-prompt/contracts.ts` for the value
|
|
1298
|
+
// shapes and the resolution rules implemented by the dispatch-path transform.
|
|
1249
1299
|
export const tenantSystemPrompts = pgTable("tenant_system_prompts", {
|
|
1250
1300
|
tenantId: tenantRefPrimaryKey(),
|
|
1251
|
-
global: jsonb("global").$type<import("../console/system-prompt/contracts").
|
|
1301
|
+
global: jsonb("global").$type<import("../console/system-prompt/contracts").ScopePromptRef | null>(),
|
|
1302
|
+
presets: jsonb("presets")
|
|
1303
|
+
.notNull()
|
|
1304
|
+
.default({})
|
|
1305
|
+
.$type<Record<string, import("../console/system-prompt/contracts").SystemPromptPreset>>(),
|
|
1252
1306
|
providers: jsonb("providers")
|
|
1253
1307
|
.notNull()
|
|
1254
1308
|
.default({})
|
|
1255
|
-
.$type<Record<string, import("../console/system-prompt/contracts").
|
|
1309
|
+
.$type<Record<string, import("../console/system-prompt/contracts").ScopePromptRef>>(),
|
|
1256
1310
|
models: jsonb("models")
|
|
1257
1311
|
.notNull()
|
|
1258
1312
|
.default({})
|
|
1259
|
-
.$type<Record<string, import("../console/system-prompt/contracts").
|
|
1313
|
+
.$type<Record<string, import("../console/system-prompt/contracts").ScopePromptRef>>(),
|
|
1314
|
+
combos: jsonb("combos")
|
|
1315
|
+
.notNull()
|
|
1316
|
+
.default({})
|
|
1317
|
+
.$type<Record<string, import("../console/system-prompt/contracts").ScopePromptRef>>(),
|
|
1318
|
+
aliases: jsonb("aliases")
|
|
1319
|
+
.notNull()
|
|
1320
|
+
.default({})
|
|
1321
|
+
.$type<Record<string, import("../console/system-prompt/contracts").ScopePromptRef>>(),
|
|
1260
1322
|
updatedAt: updatedAtColumn(),
|
|
1261
1323
|
});
|
|
1262
1324
|
|
|
@@ -1276,7 +1338,6 @@ export const inferhubConfigKeys = pgTable("inferhub_config_keys", {
|
|
|
1276
1338
|
updatedAt: updatedAtColumn(),
|
|
1277
1339
|
});
|
|
1278
1340
|
|
|
1279
|
-
|
|
1280
1341
|
// CLI Tools (CLI tool configuration): per-tenant mapping table that lets a coding-agent
|
|
1281
1342
|
// slot (e.g. Claude Code's "opus" slot) target a specific Cartethyia model. Rows
|
|
1282
1343
|
// are opaque strings — validation happens at the API boundary against the
|
|
@@ -1306,7 +1367,6 @@ export const cliToolMappings = pgTable(
|
|
|
1306
1367
|
],
|
|
1307
1368
|
);
|
|
1308
1369
|
|
|
1309
|
-
|
|
1310
1370
|
// Per-(tenant, tool, api_key) settings: whether mappings are active and which
|
|
1311
1371
|
// mode the tool is configured for. Primary key is the composite of all three.
|
|
1312
1372
|
export const cliToolSettings = pgTable(
|
|
@@ -1324,7 +1384,6 @@ export const cliToolSettings = pgTable(
|
|
|
1324
1384
|
(table) => [primaryKey({ columns: [table.tenantId, table.toolId, table.apiKeyId] })],
|
|
1325
1385
|
);
|
|
1326
1386
|
|
|
1327
|
-
|
|
1328
1387
|
// Metadata-only request telemetry. Retention is configured independently of
|
|
1329
1388
|
// payload capture (`CARTETHYIA_TELEMETRY_RETENTION_DAYS`, default 30 days).
|
|
1330
1389
|
// No prompt text, raw body, API key, encrypted-reasoning payload, or generic
|
|
@@ -1424,6 +1483,14 @@ export const telemetryEvents = pgTable(
|
|
|
1424
1483
|
* tell "not configured" from "broken".
|
|
1425
1484
|
*/
|
|
1426
1485
|
payloadCaptureState: text("payload_capture_state"),
|
|
1486
|
+
/**
|
|
1487
|
+
* Set for a completed request whose output is far smaller than what the
|
|
1488
|
+
* model can produce AND which the model ended with an ordinary `stop`
|
|
1489
|
+
* (a "degenerate stop"). Introduced to measure how often a model
|
|
1490
|
+
* truncates itself on a cached prefix — the pattern that surfaced as
|
|
1491
|
+
* short, cut-off chat replies. NULL/unset for everything else.
|
|
1492
|
+
*/
|
|
1493
|
+
degenerateStop: boolean("degenerate_stop"),
|
|
1427
1494
|
},
|
|
1428
1495
|
(table) => [
|
|
1429
1496
|
index("telemetry_events_created_at_idx").on(table.createdAt),
|
|
@@ -1473,6 +1540,22 @@ export const telemetryEvents = pgTable(
|
|
|
1473
1540
|
table.accountId,
|
|
1474
1541
|
table.createdAt,
|
|
1475
1542
|
),
|
|
1543
|
+
// Provider usage matrix + `breakdownTelemetry("provider")`: equality on
|
|
1544
|
+
// `provider_id`, range+aggregate on `created_at`. Was served by the
|
|
1545
|
+
// tenant-range index, which scanned every event in the window for one
|
|
1546
|
+
// provider's card.
|
|
1547
|
+
index("telemetry_events_tenant_provider_created_idx").on(
|
|
1548
|
+
table.tenantId,
|
|
1549
|
+
table.providerId,
|
|
1550
|
+
table.createdAt,
|
|
1551
|
+
),
|
|
1552
|
+
// `breakdownTelemetry("model")` (top-model lists): equality on
|
|
1553
|
+
// `requested_model`, range+aggregate on `created_at`.
|
|
1554
|
+
index("telemetry_events_tenant_model_created_idx").on(
|
|
1555
|
+
table.tenantId,
|
|
1556
|
+
table.requestedModel,
|
|
1557
|
+
table.createdAt,
|
|
1558
|
+
),
|
|
1476
1559
|
/**
|
|
1477
1560
|
* Serves the public share page's per-key aggregate (`share-usage.ts`):
|
|
1478
1561
|
* equality on `api_key_id`, range and aggregate on `created_at`. Without
|
|
@@ -1514,7 +1597,7 @@ export const telemetryReconciliationState = pgTable(
|
|
|
1514
1597
|
);
|
|
1515
1598
|
|
|
1516
1599
|
/** Durable aggregates retained after metadata telemetry expires. */
|
|
1517
|
-
|
|
1600
|
+
const TELEMETRY_USAGE_IDENTITY_TYPES = ["account", "api_key"] as const;
|
|
1518
1601
|
export type TelemetryUsageIdentityType = (typeof TELEMETRY_USAGE_IDENTITY_TYPES)[number];
|
|
1519
1602
|
|
|
1520
1603
|
export const telemetryUsageTotals = pgTable(
|
|
@@ -1553,7 +1636,6 @@ export const telemetryUsageTotals = pgTable(
|
|
|
1553
1636
|
],
|
|
1554
1637
|
);
|
|
1555
1638
|
|
|
1556
|
-
|
|
1557
1639
|
export const telemetryPayloads = pgTable(
|
|
1558
1640
|
"telemetry_payloads",
|
|
1559
1641
|
{
|
|
@@ -1596,8 +1678,6 @@ export const telemetryPayloads = pgTable(
|
|
|
1596
1678
|
],
|
|
1597
1679
|
);
|
|
1598
1680
|
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
1681
|
// Daily check-in ledger: one row per (account, calendar day) once the sweep has
|
|
1602
1682
|
// attempted that account's claim.
|
|
1603
1683
|
//
|
|
@@ -1610,7 +1690,9 @@ export const dailyCheckinLedger = pgTable(
|
|
|
1610
1690
|
"daily_checkin_ledger",
|
|
1611
1691
|
{
|
|
1612
1692
|
dayKey: text("day_key").notNull(),
|
|
1613
|
-
accountId: uuid("account_id")
|
|
1693
|
+
accountId: uuid("account_id")
|
|
1694
|
+
.notNull()
|
|
1695
|
+
.references(() => providerAccounts.id, { onDelete: "cascade" }),
|
|
1614
1696
|
claimedAt: timestamp("claimed_at", { withTimezone: true }).notNull().defaultNow(),
|
|
1615
1697
|
},
|
|
1616
1698
|
(table) => [
|
|
@@ -1618,7 +1700,6 @@ export const dailyCheckinLedger = pgTable(
|
|
|
1618
1700
|
],
|
|
1619
1701
|
);
|
|
1620
1702
|
|
|
1621
|
-
|
|
1622
1703
|
/**
|
|
1623
1704
|
* Per-day, per-model credit deductions the buddy billing meter reported.
|
|
1624
1705
|
*
|
|
@@ -33,7 +33,7 @@ const INVALIDATION_CHANNEL = "cartethyia:cache:invalidate";
|
|
|
33
33
|
* collide, and so a `FLUSHDB`-free deploy can drop one namespace without
|
|
34
34
|
* touching another.
|
|
35
35
|
*/
|
|
36
|
-
|
|
36
|
+
type SharedCacheNamespace = "auth" | "preferences" | "credential" | "routing" | "public-models";
|
|
37
37
|
|
|
38
38
|
const NAMESPACE_PREFIX: Readonly<Record<SharedCacheNamespace, string>> = {
|
|
39
39
|
auth: "cartethyia:cache:auth:",
|
|
@@ -50,7 +50,7 @@ const NAMESPACE_PREFIX: Readonly<Record<SharedCacheNamespace, string>> = {
|
|
|
50
50
|
};
|
|
51
51
|
|
|
52
52
|
/** One notice that peers must drop an entry. */
|
|
53
|
-
|
|
53
|
+
interface InvalidationNotice {
|
|
54
54
|
readonly namespace: SharedCacheNamespace;
|
|
55
55
|
/** Omitted means "all keys in the namespace". */
|
|
56
56
|
readonly key?: string;
|
|
@@ -27,7 +27,7 @@ export type SortOrderDb =
|
|
|
27
27
|
* `idColumn`/`sortIndexColumn`, and every caller passes its own schema-typed
|
|
28
28
|
* table plus the two columns.
|
|
29
29
|
*/
|
|
30
|
-
|
|
30
|
+
interface SortOrderTable {
|
|
31
31
|
readonly table: PgTable;
|
|
32
32
|
readonly idColumn: AnyColumn;
|
|
33
33
|
readonly sortIndexColumn: AnyColumn;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
// Persisted API-key store: the console API-key domain and gateway authentication both consume this boundary.
|
|
2
2
|
|
|
3
|
-
import { and, asc, count, desc, eq, inArray, isNotNull, isNull, lte, or, sql, type SQL } from "drizzle-orm";
|
|
3
|
+
import { and, asc, count, desc, eq, gt, inArray, isNotNull, isNull, lte, or, sql, type SQL } from "drizzle-orm";
|
|
4
4
|
import { nextSortIndex, reorderSortIndex, type SortOrderDb } from "../sort-order";
|
|
5
5
|
import { getDbHandle } from "../boot";
|
|
6
6
|
import type { CartethyiaDatabase } from "../db-handle";
|
|
7
7
|
import { lockRows } from "../dialect";
|
|
8
|
-
import { apiKeys, shareLinks, sharePopups, telemetryUsageTotals, type ApiKeyMode, type ApiKeyModelAccessMode } from "../schema";
|
|
8
|
+
import { apiKeys, shareLinks, sharePopups, telemetryUsageTotals, type ApiKeyMode, type ApiKeyModelAccessMode, type ApiKeyConcurrencyScope } from "../schema";
|
|
9
9
|
import type { AccessScope } from "../../security/access-control";
|
|
10
10
|
|
|
11
11
|
/** Sortable columns for the paged share-child listing (api_keys columns only). */
|
|
@@ -78,6 +78,7 @@ export interface ApiKeyRecord {
|
|
|
78
78
|
readonly monthlyTokenLimit?: number;
|
|
79
79
|
readonly lifetimeTokenBudget?: number;
|
|
80
80
|
readonly maxConcurrentRequests?: number;
|
|
81
|
+
readonly concurrencyScope?: ApiKeyConcurrencyScope;
|
|
81
82
|
/** Share-template recipient slot cap; undefined = unlimited. */
|
|
82
83
|
readonly maxChildren?: number;
|
|
83
84
|
readonly modelPrefix?: string;
|
|
@@ -252,6 +253,8 @@ async function writeSharePopup(
|
|
|
252
253
|
/** Explicit persistence boundary. Production must provide this; there is no fallback store. */
|
|
253
254
|
export interface ApiKeyStore {
|
|
254
255
|
list(tenantId: string): Promise<readonly ApiKeyRecord[]>;
|
|
256
|
+
/** Active, non-revoked bans for one tenant, including share children. */
|
|
257
|
+
listActiveBanned(tenantId: string): Promise<readonly ApiKeyRecord[]>;
|
|
255
258
|
get(tenantId: string, keyId: string): Promise<ApiKeyRecord | undefined>;
|
|
256
259
|
listChildren(tenantId: string, parentKeyId: string): Promise<readonly ApiKeyRecord[]>;
|
|
257
260
|
revokeChildrenByScope(
|
|
@@ -344,6 +347,9 @@ export class DrizzleApiKeyStore implements ApiKeyStore {
|
|
|
344
347
|
...(row.maxConcurrentRequests === null
|
|
345
348
|
? {}
|
|
346
349
|
: { maxConcurrentRequests: row.maxConcurrentRequests }),
|
|
350
|
+
...(row.concurrencyScope
|
|
351
|
+
? { concurrencyScope: row.concurrencyScope as ApiKeyConcurrencyScope }
|
|
352
|
+
: {}),
|
|
347
353
|
...(row.maxChildren === null ? {} : { maxChildren: row.maxChildren }),
|
|
348
354
|
...(row.modelPrefix === null ? {} : { modelPrefix: row.modelPrefix }),
|
|
349
355
|
modelAccessMode: row.modelAccessMode,
|
|
@@ -378,6 +384,22 @@ export class DrizzleApiKeyStore implements ApiKeyStore {
|
|
|
378
384
|
.limit(1000);
|
|
379
385
|
return this.withPopups(this.db, rows);
|
|
380
386
|
}
|
|
387
|
+
async listActiveBanned(tenantId: string): Promise<readonly ApiKeyRecord[]> {
|
|
388
|
+
const now = new Date();
|
|
389
|
+
const rows = await this.db
|
|
390
|
+
.select()
|
|
391
|
+
.from(apiKeys)
|
|
392
|
+
.where(
|
|
393
|
+
and(
|
|
394
|
+
eq(apiKeys.tenantId, tenantId),
|
|
395
|
+
isNotNull(apiKeys.bannedAt),
|
|
396
|
+
isNull(apiKeys.revokedAt),
|
|
397
|
+
or(isNull(apiKeys.bannedUntil), gt(apiKeys.bannedUntil, now)),
|
|
398
|
+
),
|
|
399
|
+
)
|
|
400
|
+
.orderBy(desc(apiKeys.bannedAt), apiKeys.id);
|
|
401
|
+
return rows.map((row) => this.map(row));
|
|
402
|
+
}
|
|
381
403
|
|
|
382
404
|
/** Rewrites the list order for one tenant; `keyIds` is the full order. */
|
|
383
405
|
async reorder(tenantId: string, keyIds: readonly string[]): Promise<void> {
|
|
@@ -547,6 +569,7 @@ export class DrizzleApiKeyStore implements ApiKeyStore {
|
|
|
547
569
|
monthlyTokenLimit: record.monthlyTokenLimit ?? null,
|
|
548
570
|
lifetimeTokenBudget: record.lifetimeTokenBudget ?? null,
|
|
549
571
|
maxConcurrentRequests: record.maxConcurrentRequests ?? null,
|
|
572
|
+
concurrencyScope: record.concurrencyScope ?? "family",
|
|
550
573
|
maxChildren: record.maxChildren ?? null,
|
|
551
574
|
modelPrefix: record.modelPrefix ?? null,
|
|
552
575
|
modelAccessMode: record.modelAccessMode ?? "whitelist",
|
|
@@ -615,6 +638,9 @@ export class DrizzleApiKeyStore implements ApiKeyStore {
|
|
|
615
638
|
...(patch.maxConcurrentRequests !== undefined
|
|
616
639
|
? { maxConcurrentRequests: patch.maxConcurrentRequests }
|
|
617
640
|
: {}),
|
|
641
|
+
...(patch.concurrencyScope !== undefined
|
|
642
|
+
? { concurrencyScope: patch.concurrencyScope }
|
|
643
|
+
: {}),
|
|
618
644
|
...(patch.maxChildren !== undefined ? { maxChildren: patch.maxChildren } : {}),
|
|
619
645
|
...(patch.modelPrefix !== undefined ? { modelPrefix: patch.modelPrefix } : {}),
|
|
620
646
|
...(patch.modelAccessMode !== undefined ? { modelAccessMode: patch.modelAccessMode } : {}),
|
|
@@ -802,6 +828,7 @@ export class DrizzleApiKeyStore implements ApiKeyStore {
|
|
|
802
828
|
lifetimeTokenBudget: integer("lifetime_token_budget"),
|
|
803
829
|
lifetimeTokensConsumed: integer("lifetime_tokens_consumed") ?? 0,
|
|
804
830
|
maxConcurrentRequests: integer("max_concurrent_requests"),
|
|
831
|
+
concurrencyScope: (text("concurrency_scope") ?? "family") as ApiKeyConcurrencyScope,
|
|
805
832
|
modelAccessMode: (text("model_access_mode") ?? "whitelist") as ApiKeyModelAccessMode,
|
|
806
833
|
modelList: json("model_list"),
|
|
807
834
|
clientRouterDenylist: json("client_router_denylist"),
|