cartethyia 2.7.3 → 2.7.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +2 -0
- package/README.md +17 -5
- package/dist/dashboard/assets/{BannedPage-2TJD37Hb.js → BannedPage-Cj15GsMp.js} +1 -1
- package/dist/dashboard/assets/BannedPage-Cj15GsMp.js.gz +0 -0
- package/dist/dashboard/assets/ClaimGiftsButton-D83pBJ6J.js +1 -0
- package/dist/dashboard/assets/ClaimGiftsButton-D83pBJ6J.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolDetailPage-xefJNRmJ.js → CliToolDetailPage-DZKmVuJN.js} +1 -1
- package/dist/dashboard/assets/CliToolDetailPage-DZKmVuJN.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolsPage-Rj9iHQ3F.js → CliToolsPage-BIccZmeL.js} +1 -1
- package/dist/dashboard/assets/CliToolsPage-BIccZmeL.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-DEEKkYD-.js +1 -0
- package/dist/dashboard/assets/CombosPage-DEEKkYD-.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-D5rol2j2.js +1 -0
- package/dist/dashboard/assets/ConsoleLogPage-D5rol2j2.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-BKj_r0rp.js +1 -0
- package/dist/dashboard/assets/CustomizationPage-BKj_r0rp.js.gz +0 -0
- package/dist/dashboard/assets/InjectSystemPromptPanel-B5nZvmf0.js +656 -0
- package/dist/dashboard/assets/InjectSystemPromptPanel-B5nZvmf0.js.gz +0 -0
- package/dist/dashboard/assets/{LoginPage-C4w7vea-.js → LoginPage-DkXb6dx-.js} +1 -1
- package/dist/dashboard/assets/LoginPage-DkXb6dx-.js.gz +0 -0
- package/dist/dashboard/assets/{ModelPicker-BtPcAfot.js → ModelPicker-DnQPSIA8.js} +1 -1
- package/dist/dashboard/assets/ModelPicker-DnQPSIA8.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-Ci2wQKUz.js +1 -0
- package/dist/dashboard/assets/OverviewPage-Ci2wQKUz.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-odlgYrOg.js +5 -0
- package/dist/dashboard/assets/ProviderDetailPage-odlgYrOg.js.gz +0 -0
- package/dist/dashboard/assets/ProviderIcon-EfeloBn4.js +1 -0
- package/dist/dashboard/assets/ProviderIcon-EfeloBn4.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-CqFBQEB8.js +1 -0
- package/dist/dashboard/assets/ProvidersPage-CqFBQEB8.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-5SnUSy3j.js +9 -0
- package/dist/dashboard/assets/ProxyPage-5SnUSy3j.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-CmnMDzhP.js +1 -0
- package/dist/dashboard/assets/QuotaPage-CmnMDzhP.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-BJtRZ4qD.js +1 -0
- package/dist/dashboard/assets/SettingsPage-BJtRZ4qD.js.gz +0 -0
- package/dist/dashboard/assets/{SetupPage-BQS0Rx_u.js → SetupPage-WTa0sktC.js} +1 -1
- package/dist/dashboard/assets/SetupPage-WTa0sktC.js.gz +0 -0
- package/dist/dashboard/assets/{StudioPage-TP7voAQ0.js → StudioPage-BfAb5-o_.js} +3 -3
- package/dist/dashboard/assets/StudioPage-BfAb5-o_.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-BNzeZ2Vk.js +56 -0
- package/dist/dashboard/assets/UsagePage-BNzeZ2Vk.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-l29JROPv.js +1 -0
- package/dist/dashboard/assets/api-keys-l29JROPv.js.gz +0 -0
- package/dist/dashboard/assets/app--PJWq8_C.css +2 -0
- package/dist/dashboard/assets/app--PJWq8_C.css.gz +0 -0
- package/dist/dashboard/assets/{assert-C0YYuZJ4.js → assert-C0K-c4eo.js} +1 -1
- package/dist/dashboard/assets/assert-C0K-c4eo.js.gz +0 -0
- package/dist/dashboard/assets/{badge-B_DMpMJN.js → badge-DfbhQU4k.js} +1 -1
- package/dist/dashboard/assets/badge-DfbhQU4k.js.gz +0 -0
- package/dist/dashboard/assets/{banner-DMrMPmth.js → banner-BllT4LcF.js} +1 -1
- package/dist/dashboard/assets/banner-BllT4LcF.js.gz +0 -0
- package/dist/dashboard/assets/brand/login_register.jpg +0 -0
- package/dist/dashboard/assets/brand/login_register_initial.jpg +0 -0
- package/dist/dashboard/assets/brand/og_bansos.jpg +0 -0
- package/dist/dashboard/assets/brand/og_image.jpg +0 -0
- package/dist/dashboard/assets/{card-Be5Uzamx.js → card-CtkX66Wd.js} +1 -1
- package/dist/dashboard/assets/card-CtkX66Wd.js.gz +0 -0
- package/dist/dashboard/assets/chevron-left-Bf6CaTSJ.js +1 -0
- package/dist/dashboard/assets/chevron-left-Bf6CaTSJ.js.gz +0 -0
- package/dist/dashboard/assets/{cli-tools-muFRphDu.js → cli-tools-CQXVMn4P.js} +1 -1
- package/dist/dashboard/assets/cli-tools-CQXVMn4P.js.gz +0 -0
- package/dist/dashboard/assets/{clipboard-button-DOfrVmsi.js → clipboard-button-BQqBQd2n.js} +1 -1
- package/dist/dashboard/assets/clipboard-button-BQqBQd2n.js.gz +0 -0
- package/dist/dashboard/assets/{dialog-C9omdjQt.js → dialog-BEK7KP0H.js} +1 -1
- package/dist/dashboard/assets/dialog-BEK7KP0H.js.gz +0 -0
- package/dist/dashboard/assets/{drawer-Bm8WKaGY.js → drawer-BrTEZ8zu.js} +1 -1
- package/dist/dashboard/assets/drawer-BrTEZ8zu.js.gz +0 -0
- package/dist/dashboard/assets/entry-CHWxbS56.js +1 -0
- package/dist/dashboard/assets/entry-CHWxbS56.js.gz +0 -0
- package/dist/dashboard/assets/{entry-q5HwZ8nN.js → entry-EBHJ3DrE.js} +1 -1
- package/dist/dashboard/assets/entry-EBHJ3DrE.js.gz +0 -0
- package/dist/dashboard/assets/entry-LCS85hiq.js +4 -0
- package/dist/dashboard/assets/entry-LCS85hiq.js.gz +0 -0
- package/dist/dashboard/assets/{formatters-BTScrQpe.js → formatters-BAACA727.js} +1 -1
- package/dist/dashboard/assets/formatters-BAACA727.js.gz +0 -0
- package/dist/dashboard/assets/{github-badge-Orjuzemr.js → github-badge-YSCE4UC2.js} +1 -1
- package/dist/dashboard/assets/github-badge-YSCE4UC2.js.gz +0 -0
- package/dist/dashboard/assets/{index-cPrX5vn6.js → index-FQlj0HUT.js} +2 -2
- package/dist/dashboard/assets/index-FQlj0HUT.js.gz +0 -0
- package/dist/dashboard/assets/{inline-Dvux8w-0.js → inline-C4gU5iOL.js} +1 -1
- package/dist/dashboard/assets/inline-C4gU5iOL.js.gz +0 -0
- package/dist/dashboard/assets/{input-CU1Rgf3p.js → input-BHHX_Tmm.js} +1 -1
- package/dist/dashboard/assets/input-BHHX_Tmm.js.gz +0 -0
- package/dist/dashboard/assets/{layout-C_7A6H55.js → layout-CajrYUnD.js} +1 -1
- package/dist/dashboard/assets/{layout-C_7A6H55.js.gz → layout-CajrYUnD.js.gz} +0 -0
- package/dist/dashboard/assets/{list-row-CzFvogbj.js → list-row-Bx2I7XUe.js} +1 -1
- package/dist/dashboard/assets/list-row-Bx2I7XUe.js.gz +0 -0
- package/dist/dashboard/assets/live-DNNqRbj2.js +1 -0
- package/dist/dashboard/assets/live-DNNqRbj2.js.gz +0 -0
- package/dist/dashboard/assets/minimize-2-BTmMTK4o.js +1 -0
- package/dist/dashboard/assets/minimize-2-BTmMTK4o.js.gz +0 -0
- package/dist/dashboard/assets/{mutation-JJGQm8TD.js → mutation-Rr0TIvcj.js} +1 -1
- package/dist/dashboard/assets/mutation-Rr0TIvcj.js.gz +0 -0
- package/dist/dashboard/assets/network-C5UBACrJ.js +1 -0
- package/dist/dashboard/assets/network-C5UBACrJ.js.gz +0 -0
- package/dist/dashboard/assets/numbers-CUWAZK4J.js +2 -0
- package/dist/dashboard/assets/numbers-CUWAZK4J.js.gz +0 -0
- package/dist/dashboard/assets/pagination-Xsi9Qx04.js +1 -0
- package/dist/dashboard/assets/pagination-Xsi9Qx04.js.gz +0 -0
- package/dist/dashboard/assets/{query-keys-CLaNz8Xz.js → query-keys-BloKtaXJ.js} +1 -1
- package/dist/dashboard/assets/query-keys-BloKtaXJ.js.gz +0 -0
- package/dist/dashboard/assets/quota-DvQbD_sr.js +1 -0
- package/dist/dashboard/assets/quota-DvQbD_sr.js.gz +0 -0
- package/dist/dashboard/assets/{routing-BEH32QOC.js → routing-in0f_IfK.js} +1 -1
- package/dist/dashboard/assets/routing-in0f_IfK.js.gz +0 -0
- package/dist/dashboard/assets/{select-B3iTetHN.js → select-BLULNqTp.js} +2 -2
- package/dist/dashboard/assets/select-BLULNqTp.js.gz +0 -0
- package/dist/dashboard/assets/settings-CcGczj6I.js +1 -0
- package/dist/dashboard/assets/settings-CcGczj6I.js.gz +0 -0
- package/dist/dashboard/assets/shield-BMQNxORh.js +1 -0
- package/dist/dashboard/assets/shield-BMQNxORh.js.gz +0 -0
- package/dist/dashboard/assets/{stack-KrOrm7JZ.js → stack-D2lIR0tg.js} +1 -1
- package/dist/dashboard/assets/stack-D2lIR0tg.js.gz +0 -0
- package/dist/dashboard/assets/{state-DLS7IDMy.js → state-DTARjfiW.js} +1 -1
- package/dist/dashboard/assets/state-DTARjfiW.js.gz +0 -0
- package/dist/dashboard/assets/{switch-42ItvbAs.js → switch-c0oPSnqW.js} +1 -1
- package/dist/dashboard/assets/switch-c0oPSnqW.js.gz +0 -0
- package/dist/dashboard/assets/{useMutation-Dmnfh1Ue.js → useMutation-m8WGNPrz.js} +1 -1
- package/dist/dashboard/assets/useMutation-m8WGNPrz.js.gz +0 -0
- package/dist/dashboard/console-backgrounds.jpg +0 -0
- package/dist/dashboard/console-backgrounds_initial.jpg +0 -0
- package/dist/dashboard/index.html +2 -2
- package/dist/dashboard/index.html.gz +0 -0
- package/dist/dashboard/precompressed-assets.json +59 -57
- package/migrations/full-postgres/0027_qoder_identity_genuine_seed.sql +9 -10
- package/migrations/full-postgres/0032_add_provider_pool_accounts.sql +53 -0
- package/migrations/full-postgres/0033_add_api_key_ban.sql +9 -0
- package/migrations/full-postgres/0034_add_api_key_ban_until.sql +7 -0
- package/migrations/full-postgres/0035_align_pool_account_status_enum.sql +21 -0
- package/migrations/full-postgres/0036_system_prompt_presets_refs.sql +10 -0
- package/migrations/full-postgres/0037_share_concurrency_scope.sql +3 -0
- package/migrations/full-postgres/0038_add_accounts_quota_snapshot.sql +7 -0
- package/migrations/full-postgres/0039_add_telemetry_provider_model_indexes.sql +13 -0
- package/migrations/full-postgres/0040_add_models_dialect.sql +3 -0
- package/migrations/full-postgres/0041_add_daily_checkin_ledger_account_fk.sql +7 -0
- package/migrations/lite-sqlite3/0027_qoder_identity_genuine_seed.sql +9 -10
- package/migrations/lite-sqlite3/0032_add_provider_pool_accounts.sql +44 -0
- package/migrations/lite-sqlite3/0033_add_api_key_ban.sql +8 -0
- package/migrations/lite-sqlite3/0034_add_api_key_ban_until.sql +5 -0
- package/migrations/lite-sqlite3/0035_align_pool_account_status_enum.sql +6 -0
- package/migrations/lite-sqlite3/0036_system_prompt_presets_refs.sql +14 -0
- package/migrations/lite-sqlite3/0037_share_concurrency_scope.sql +3 -0
- package/migrations/lite-sqlite3/0038_add_accounts_quota_snapshot.sql +4 -0
- package/migrations/lite-sqlite3/0039_add_telemetry_provider_model_indexes.sql +6 -0
- package/migrations/lite-sqlite3/0040_add_models_dialect.sql +3 -0
- package/migrations/lite-sqlite3/0041_add_daily_checkin_ledger_account_fk.sql +26 -0
- package/package.json +4 -5
- package/scripts/cli/update.ts +15 -41
- package/src/boot/app.ts +67 -10
- package/src/boot/dependencies.ts +786 -732
- package/src/boot/lifecycle.ts +11 -12
- package/src/config/cooldowns.ts +34 -55
- package/src/config/network.ts +8 -4
- package/src/config/package-paths.ts +1 -1
- package/src/config/providers.ts +13 -14
- package/src/config/resources.ts +0 -5
- package/src/config/runtime.ts +2 -5
- package/src/config/spec.ts +26 -6
- package/src/console/api-keys/ban-constants.ts +38 -0
- package/src/console/api-keys/contracts.ts +92 -31
- package/src/console/api-keys/routes.ts +131 -6
- package/src/console/audit/routes.ts +3 -3
- package/src/console/auth/access.ts +4 -4
- package/src/console/auth/account-reset.ts +5 -5
- package/src/console/auth/audit-recorder.ts +1 -1
- package/src/console/auth/lockout.ts +7 -3
- package/src/console/auth/row-coercion.ts +57 -12
- package/src/console/auth/session-resolver.ts +1 -1
- package/src/console/auth/session-service.ts +8 -2
- package/src/console/auth/session.ts +40 -34
- package/src/console/auth/setup-service.ts +4 -3
- package/src/console/auth/user-store.ts +1 -1
- package/src/console/backup/9router-map.ts +0 -3
- package/src/console/backup/contracts.ts +11 -9
- package/src/console/backup/export-endpoint.ts +11 -12
- package/src/console/backup/nine-router.ts +1 -7
- package/src/console/backup/routes.ts +32 -36
- package/src/console/backup/service.ts +468 -468
- package/src/console/backup/store.ts +47 -29
- package/src/console/backup/validate.ts +11 -1
- package/src/console/cli-tools/contracts.ts +7 -7
- package/src/console/cli-tools/injectors/cline.ts +110 -110
- package/src/console/cli-tools/injectors/codex.ts +181 -181
- package/src/console/cli-tools/injectors/copilot.ts +108 -108
- package/src/console/cli-tools/injectors/driver.ts +247 -247
- package/src/console/cli-tools/injectors/jcode.ts +115 -115
- package/src/console/cli-tools/injectors/openclaw.ts +161 -161
- package/src/console/cli-tools/injectors/opencode.ts +153 -153
- package/src/console/cli-tools/routes.ts +4 -4
- package/src/console/cli-tools/service.ts +1 -1
- package/src/console/cli-tools/store.ts +2 -2
- package/src/console/console-router-guards.ts +2 -2
- package/src/console/console-router.ts +36 -4
- package/src/console/dashboard-assets.ts +9 -9
- package/src/console/data-management/routes.ts +3 -3
- package/src/console/domain-registration.ts +18 -4
- package/src/console/inferhub/client.ts +0 -7
- package/src/console/inferhub/key-store.ts +1 -1
- package/src/console/inferhub/normalize.ts +2 -2
- package/src/console/inferhub/routes.ts +1 -1
- package/src/console/inferhub/types.ts +1 -1
- package/src/console/observability/contracts.ts +5 -1
- package/src/console/observability/live.ts +34 -21
- package/src/console/observability/logs.ts +4 -4
- package/src/console/observability/performance.ts +3 -3
- package/src/console/observability/routes.ts +3 -3
- package/src/console/observability/sse.ts +62 -8
- package/src/console/observability/store.ts +36 -20
- package/src/console/providers/catalog/catalog-projections.ts +1 -1
- package/src/console/providers/catalog/contracts.ts +82 -22
- package/src/console/providers/catalog/model-operations.ts +1 -1
- package/src/console/providers/catalog/provider-operations.ts +110 -17
- package/src/console/providers/catalog/public-model-store.ts +0 -0
- package/src/console/providers/catalog/routes.ts +30 -6
- package/src/console/providers/catalog/store-helpers.ts +349 -326
- package/src/console/providers/catalog/store.ts +1662 -1292
- package/src/console/providers/detail/routes.ts +4 -4
- package/src/console/providers/oauth/callback-listener.ts +100 -18
- package/src/console/providers/oauth/routes.ts +95 -16
- package/src/console/quota/account-quota-global.ts +41 -3
- package/src/console/quota/account-quota-shared.ts +10 -3
- package/src/console/quota/account-quota-tenant.ts +285 -67
- package/src/console/quota/account-test-egress.ts +4 -4
- package/src/console/quota/daily-growth-job.ts +308 -0
- package/src/console/quota/native-identity.ts +2 -2
- package/src/console/quota/quota-view.ts +1 -1
- package/src/console/remote-support/routes.ts +1 -1
- package/src/console/remote-support/service.ts +2 -2
- package/src/console/routing/model/contracts.ts +1 -1
- package/src/console/routing/model/routes.ts +3 -3
- package/src/console/routing/pools/contracts.ts +13 -10
- package/src/console/routing/pools/import-parse.ts +3 -3
- package/src/console/routing/pools/operations.ts +14 -7
- package/src/console/routing/pools/probe-verdict.ts +8 -3
- package/src/console/routing/pools/relay-deploy.ts +12 -12
- package/src/console/routing/pools/routes.ts +3 -3
- package/src/console/routing/pools/store.ts +21 -1
- package/src/console/settings/contracts.ts +29 -2
- package/src/console/settings/routes.ts +3 -3
- package/src/console/settings/store.ts +20 -0
- package/src/console/share/share-router.ts +112 -44
- package/src/console/share/share-stats.ts +109 -39
- package/src/console/share/share-usage.ts +1 -1
- package/src/console/shared/errors.ts +2 -2
- package/src/console/shared/object.ts +18 -0
- package/src/console/shared/read-through-cache.ts +42 -6
- package/src/console/shared/transaction.ts +14 -0
- package/src/console/system-prompt/contracts.ts +89 -12
- package/src/console/system-prompt/routes.ts +98 -45
- package/src/console/updates/apply-update.ts +244 -0
- package/src/console/updates/routes.ts +256 -98
- package/src/main.ts +11 -5
- package/src/network/http2-bidi.ts +1 -1
- package/src/network/ip-utils.ts +298 -0
- package/src/network/outbound-fetch.ts +76 -5
- package/src/network/pool/agent.ts +7 -6
- package/src/network/pool/byte-accounting.ts +1 -1
- package/src/network/pool/resolver.ts +55 -51
- package/src/network/pool/selector.ts +150 -136
- package/src/network/pool-cooldown-store.ts +71 -16
- package/src/network/pool-health-machine.ts +22 -2
- package/src/network/rate-limit-verdict.ts +68 -0
- package/src/network/retry.ts +1 -1
- package/src/network/ssrf.ts +24 -75
- package/src/observability/event-log.ts +2 -2
- package/src/observability/event-query.ts +286 -214
- package/src/observability/log-sanitize.ts +1 -1
- package/src/observability/metrics.ts +4 -4
- package/src/observability/payload-capture.ts +6 -5
- package/src/observability/payload-store.ts +1 -1
- package/src/observability/runtime-metrics.ts +1 -1
- package/src/observability/telemetry-buffer.ts +12 -3
- package/src/observability/telemetry-reconciliation.ts +3 -3
- package/src/observability/telemetry-status.ts +1 -1
- package/src/observability/token-speed.ts +1 -1
- package/src/persistence/connection-url.ts +1 -1
- package/src/persistence/db-handle.ts +1 -1
- package/src/persistence/db-pg.ts +2 -2
- package/src/persistence/db-sqlite.ts +10 -5
- package/src/persistence/dialect.ts +5 -1
- package/src/persistence/readiness.ts +19 -7
- package/src/persistence/redis.ts +1 -1
- package/src/persistence/schema.ts +177 -45
- package/src/persistence/shared-cache.ts +2 -2
- package/src/persistence/sort-order.ts +1 -1
- package/src/persistence/stores/api-key-store.ts +109 -4
- package/src/persistence/stores/share-store.ts +20 -8
- package/src/persistence/stores/telemetry-store.ts +1 -1
- package/src/persistence/system-prompt-store.ts +63 -18
- package/src/persistence/tenant-preferences.ts +1 -1
- package/src/persistence/ttl-cache.ts +3 -3
- package/src/persistence/usage-flush.ts +7 -2
- package/src/protocol/canonical-model.ts +1 -1
- package/src/protocol/codecs.ts +12 -16
- package/src/protocol/dsml.ts +3 -49
- package/src/protocol/gateway-error.ts +6 -3
- package/src/protocol/image-clamp.ts +2 -2
- package/src/protocol/image-generation.ts +2 -2
- package/src/protocol/reasoning-text.ts +1 -1
- package/src/protocol/reasoning.ts +7 -52
- package/src/protocol/registry.ts +50 -15
- package/src/protocol/request/chat.ts +46 -19
- package/src/protocol/request/codex.ts +1 -1
- package/src/protocol/request/messages.ts +46 -14
- package/src/protocol/request/responses.ts +19 -10
- package/src/protocol/response/chat.ts +27 -12
- package/src/protocol/response/codex.ts +2 -2
- package/src/protocol/response/gemini.ts +1 -1
- package/src/protocol/response/messages.ts +6 -6
- package/src/protocol/response/omp-tool-call-markup.ts +1 -1
- package/src/protocol/response/responses.ts +3 -3
- package/src/protocol/sse-decoder.ts +64 -4
- package/src/protocol/stop-reason.ts +2 -2
- package/src/protocol/think-split.ts +347 -0
- package/src/protocol/translation/attachment-integrity.ts +3 -3
- package/src/protocol/translation/cache-controls.ts +8 -20
- package/src/protocol/translation/capabilities.ts +8 -8
- package/src/protocol/translation/dialect/deepseek.ts +10 -0
- package/src/protocol/translation/dialect/inband.ts +39 -0
- package/src/protocol/translation/dialect/model-thinking.ts +89 -0
- package/src/protocol/translation/dialect/qwen.ts +114 -0
- package/src/protocol/translation/dialect/resolver.ts +83 -0
- package/src/protocol/translation/dialect/rules.ts +93 -0
- package/src/protocol/translation/dialect/thinking-ladder.ts +245 -0
- package/src/protocol/translation/dialect/types.ts +58 -0
- package/src/protocol/translation/quirks.ts +18 -4
- package/src/protocol/translation/thinking.ts +12 -252
- package/src/protocol/translation/tool-id.ts +3 -3
- package/src/protocol/translation/tool-repair.ts +1 -1
- package/src/protocol/upstream/post-json.ts +6 -1
- package/src/protocol/upstream/send-claude-messages.ts +3 -3
- package/src/providers/adapters/base-adapter.ts +1 -1
- package/src/providers/adapters/compatible-adapter.ts +44 -5
- package/src/providers/authentication/jwt-validator.ts +0 -5
- package/src/providers/authentication/oauth-client.ts +254 -253
- package/src/providers/authentication/oauth-flow-store.ts +42 -7
- package/src/providers/authentication/oauth-refresh-service.ts +2 -2
- package/src/providers/bundled-discovery-providers.ts +6 -30
- package/src/providers/configured-openai-providers.ts +82 -97
- package/src/providers/configured-provider.ts +1 -1
- package/src/providers/default-registry.ts +23 -35
- package/src/providers/discovery/catalog/catalog-format.ts +13 -2
- package/src/providers/discovery/discovery-types.ts +0 -1
- package/src/providers/discovery/openai-model-discovery.ts +2 -2
- package/src/providers/discovery/probe-phases.ts +50 -15
- package/src/providers/discovery/probing-service.ts +18 -20
- package/src/providers/integrations/agentrouter/agentrouter.ts +14 -5
- package/src/providers/integrations/alibabacp/alibabacp.ts +2 -2
- package/src/providers/integrations/anthropic.ts +4 -11
- package/src/providers/integrations/antigravity/antigravity-oauth.ts +0 -1
- package/src/providers/integrations/antigravity/antigravity-protocol.ts +0 -14
- package/src/providers/integrations/antigravity/antigravity.ts +15 -26
- package/src/providers/integrations/buddy/buddy-catalog-shared.ts +2 -2
- package/src/providers/integrations/buddy/buddy-chat-shared.ts +20 -4
- package/src/providers/integrations/buddy/buddy-cli-headers.ts +19 -52
- package/src/providers/integrations/buddy/buddy-discovery-shared.ts +37 -6
- package/src/providers/integrations/buddy/buddy-identity.ts +108 -1
- package/src/providers/integrations/buddy/buddy-media.ts +16 -16
- package/src/providers/integrations/buddy/buddy-oauth-shared.ts +1 -1
- package/src/providers/integrations/buddy/codebuddy/cn-provider.ts +1 -2
- package/src/providers/integrations/buddy/codebuddy/provider.ts +5 -16
- package/src/providers/integrations/buddy/codebuddy/shared.ts +30 -9
- package/src/providers/integrations/buddy/workbuddy/provider.ts +6 -10
- package/src/providers/integrations/buddy/workbuddy/shared.ts +21 -3
- package/src/providers/integrations/claude/claude-betas.ts +1 -1
- package/src/providers/integrations/claude/claude-cch.ts +148 -148
- package/src/providers/integrations/claude/claude-credentials.ts +1 -1
- package/src/providers/integrations/claude/claude-slow-lane-retry.ts +1 -1
- package/src/providers/integrations/claude/claude.ts +3 -10
- package/src/providers/integrations/claude/fallback-credit.ts +3 -7
- package/src/providers/integrations/cline/cline.ts +2 -2
- package/src/providers/integrations/cloudflareai/cloudflareai.ts +4 -7
- package/src/providers/integrations/codex/codex-device-code.ts +101 -101
- package/src/providers/integrations/codex/codex-errors.ts +1 -1
- package/src/providers/integrations/codex/codex-oauth.ts +0 -1
- package/src/providers/integrations/codex/codex.ts +14 -15
- package/src/providers/integrations/commandcode/commandcode.ts +76 -116
- package/src/providers/integrations/commandcode/quota.ts +132 -48
- package/src/providers/integrations/cursor/cursor-http2.ts +11 -14
- package/src/providers/integrations/cursor/cursor-quota.ts +2 -6
- package/src/providers/integrations/cursor/cursor-request.ts +0 -87
- package/src/providers/integrations/cursor/cursor-shared.ts +0 -4
- package/src/providers/integrations/cursor/cursor.ts +53 -20
- package/src/providers/integrations/cursor/protocol/framing.ts +0 -56
- package/src/providers/integrations/cursor/protocol/messages.ts +0 -96
- package/src/providers/integrations/factory/factory.ts +13 -13
- package/src/providers/integrations/gemini.ts +11 -11
- package/src/providers/integrations/github/github-discovery.ts +55 -10
- package/src/providers/integrations/github/github-oauth.ts +6 -5
- package/src/providers/integrations/github/github.ts +47 -20
- package/src/providers/integrations/grok/grok-oauth.ts +261 -261
- package/src/providers/integrations/grok/grok-quota-frame.ts +1 -1
- package/src/providers/integrations/grok/grok-quota.ts +16 -7
- package/src/providers/integrations/grok/grok-ratelimit-store.ts +0 -5
- package/src/providers/integrations/grok/grok-ratelimit.ts +3 -0
- package/src/providers/integrations/grok/grok.ts +88 -42
- package/src/providers/integrations/inferhub.ts +4 -11
- package/src/providers/integrations/kilo/kilo-oauth.ts +1 -1
- package/src/providers/integrations/kimi/kimi.ts +4 -11
- package/src/providers/integrations/kiro/kiro-cache.ts +31 -0
- package/src/providers/integrations/kiro/kiro-catalog.ts +31 -5
- package/src/providers/integrations/kiro/kiro-discovery.ts +47 -43
- package/src/providers/integrations/kiro/kiro-machine-id.ts +6 -3
- package/src/providers/integrations/kiro/kiro-oauth.ts +523 -255
- package/src/providers/integrations/kiro/kiro-quota.ts +35 -47
- package/src/providers/integrations/kiro/kiro-request.ts +8 -11
- package/src/providers/integrations/kiro/kiro-stream.ts +13 -0
- package/src/providers/integrations/kiro/kiro.ts +91 -78
- package/src/providers/integrations/muse/muse-oauth.ts +0 -1
- package/src/providers/integrations/muse/muse-quota.ts +0 -4
- package/src/providers/integrations/muse/muse.ts +6 -9
- package/src/providers/integrations/opencode/fingerprint.ts +7 -5
- package/src/providers/integrations/opencode/provider.ts +17 -24
- package/src/providers/integrations/perplexity.ts +2 -2
- package/src/providers/integrations/qoder/qoder-catalog.ts +132 -17
- package/src/providers/integrations/qoder/qoder-checkin.ts +26 -8
- package/src/providers/integrations/qoder/qoder-native-identity-store.ts +9 -8
- package/src/providers/integrations/qoder/qoder-oauth.ts +165 -25
- package/src/providers/integrations/qoder/qoder-quota.ts +48 -6
- package/src/providers/integrations/qoder/qoder-realm.ts +22 -28
- package/src/providers/integrations/qoder/qoder-sse.ts +159 -0
- package/src/providers/integrations/qoder/qoder-umid.ts +33 -38
- package/src/providers/integrations/qoder/qoder-virtual.ts +14 -2
- package/src/providers/integrations/qoder/qoder.ts +521 -63
- package/src/providers/integrations/shared/connect.ts +0 -54
- package/src/providers/integrations/shared/hash.ts +15 -0
- package/src/providers/integrations/shared/session-cache.ts +136 -0
- package/src/providers/integrations/tokenharbor.ts +2 -2
- package/src/providers/integrations/trae/trae-cache.ts +7 -87
- package/src/providers/integrations/trae/trae-catalog.ts +1 -6
- package/src/providers/integrations/trae/trae-protocol.ts +17 -8
- package/src/providers/integrations/trae/trae-realm.ts +0 -7
- package/src/providers/integrations/trae/trae.ts +59 -19
- package/src/providers/integrations/xai/xai-oauth.ts +5 -5
- package/src/providers/integrations/zcode/oauth.ts +1 -1
- package/src/providers/integrations/zcode-start-plan/gifts.ts +265 -0
- package/src/providers/integrations/zcode-start-plan/oauth.ts +154 -0
- package/src/providers/integrations/zcode-start-plan/provider.ts +131 -0
- package/src/providers/integrations/zcode-start-plan/quota.ts +152 -0
- package/src/providers/integrations/zcode-start-plan/zcode_prompt.json +10 -0
- package/src/providers/operations/account-error-classification.ts +131 -116
- package/src/providers/operations/account-health-service.ts +31 -39
- package/src/providers/operations/account-reset-service.ts +58 -1
- package/src/providers/operations/cli-platform.ts +31 -5
- package/src/providers/operations/client-version-resolver.ts +4 -6
- package/src/providers/operations/client-versions.ts +10 -7
- package/src/providers/operations/provider-catalog-seeder.ts +2 -0
- package/src/providers/operations/provider-catalog-service.ts +22 -6
- package/src/providers/operations/provider-credential-service.ts +35 -6
- package/src/providers/operations/upstream-deadline.ts +3 -3
- package/src/providers/provider-contracts.ts +65 -10
- package/src/providers/provider-credential-resolver.ts +1 -1
- package/src/providers/provider-metadata.ts +17 -2
- package/src/providers/provider-model-definition.ts +22 -1
- package/src/providers/provider-probe.ts +4 -2
- package/src/providers/provider-registry.ts +4 -4
- package/src/providers/provider-usage.ts +81 -16
- package/src/providers/quota/cache.ts +29 -0
- package/src/providers/quota/quota-contracts.ts +28 -10
- package/src/providers/quota/refresh.ts +29 -6
- package/src/providers/quota/snapshot.ts +94 -0
- package/src/providers/search/search-provider.ts +6 -8
- package/src/providers/search/search-providers.ts +2 -1
- package/src/security/abuse.ts +8 -8
- package/src/security/admission/reasons.ts +17 -17
- package/src/security/admission/service.ts +2 -2
- package/src/security/admission/ttl.ts +61 -61
- package/src/security/api-key-auth.ts +20 -8
- package/src/security/client-router-fingerprint.ts +4 -4
- package/src/security/credential-codec.ts +3 -3
- package/src/security/csrf.ts +2 -2
- package/src/security/ip-boundary.ts +16 -189
- package/src/security/model-access-rule.ts +1 -1
- package/src/security/outbound-headers.ts +18 -0
- package/src/shared/coerce.ts +19 -0
- package/src/shared/invoke-guard.ts +47 -0
- package/src/transport/contracts.ts +13 -4
- package/src/transport/dispatch/attempt-finalize.ts +5 -4
- package/src/transport/dispatch/attempt-loop.ts +10 -7
- package/src/transport/dispatch/capture-policy.ts +34 -37
- package/src/transport/dispatch/empty-stream.ts +28 -25
- package/src/transport/dispatch/generation.ts +33 -53
- package/src/transport/dispatch/leases.ts +8 -8
- package/src/transport/dispatch/native-shared.ts +127 -0
- package/src/transport/dispatch/proxy-request.ts +10 -8
- package/src/transport/dispatch/responses-compact.ts +35 -56
- package/src/transport/dispatch/retry-policy.ts +4 -67
- package/src/transport/dispatch/stream-bridge.ts +1 -1
- package/src/transport/dispatch/streaming/encoder.ts +18 -72
- package/src/transport/dispatch/streaming/finalize.ts +11 -10
- package/src/transport/dispatch/streaming/stream.ts +7 -3
- package/src/transport/dispatch/streaming/thinking-loop.ts +1 -1
- package/src/transport/dispatch/systemone.ts +27 -55
- package/src/transport/dispatch/token-count.ts +49 -46
- package/src/transport/dispatch/websearch.ts +24 -50
- package/src/transport/drain-endpoint.ts +3 -3
- package/src/transport/failure-policy.ts +19 -6
- package/src/transport/guards/auth-guard.ts +6 -11
- package/src/transport/guards/body-policy.ts +7 -7
- package/src/transport/guards/error-lifecycle.ts +18 -3
- package/src/transport/guards/guard-errors.ts +7 -3
- package/src/transport/guards/headroom-guard.ts +2 -2
- package/src/transport/guards/pipeline.ts +7 -7
- package/src/transport/guards/readiness-guard.ts +16 -2
- package/src/transport/guards/request-context.ts +15 -20
- package/src/transport/request/preparer.ts +306 -27
- package/src/transport/request/rtk/compress-request.ts +3 -3
- package/src/transport/request/rtk/rtk-constants.ts +2 -2
- package/src/transport/request/rtk/rtk-filters.ts +8 -8
- package/src/transport/request/state.ts +5 -20
- package/src/transport/request/system-prompt-injection.ts +74 -11
- package/src/transport/request/tenant-preferences.ts +22 -0
- package/src/transport/routing/candidate-index.ts +2 -2
- package/src/transport/routing/round-robin.ts +1 -1
- package/src/transport/routing/route-catalog.ts +113 -19
- package/src/transport/routing/route-model.ts +131 -38
- package/src/transport/routing/router.ts +154 -39
- package/src/transport/shutdown-notice.ts +21 -9
- package/src/transport/surface/adapters.ts +3 -3
- package/src/transport/surface/chat/encode.ts +7 -7
- package/src/transport/surface/chat/parse.ts +7 -7
- package/src/transport/surface/completion.ts +3 -3
- package/src/transport/surface/content-parts.ts +1 -1
- package/src/transport/surface/dialects.ts +2 -2
- package/src/transport/surface/messages/encode.ts +1 -1
- package/src/transport/surface/messages/parse.ts +3 -3
- package/src/transport/surface/responses/adapter.ts +1 -1
- package/src/transport/surface/responses/errors.ts +0 -9
- package/src/transport/vision-handoff-cache.ts +58 -0
- package/src/transport/vision-handoff.ts +376 -0
- package/src/workers/account-identity-sweep.ts +2 -2
- package/src/workers/buddy-growth-tasks.ts +392 -346
- package/src/workers/daily-checkin.ts +102 -49
- package/src/workers/maintenance-lease.ts +2 -35
- package/src/workers/oauth-refresh-sweep.ts +8 -1
- package/src/workers/quota-refresh-sweep.ts +120 -82
- package/src/workers/scheduler.ts +67 -4
- package/src/workers/sweep-runner.ts +24 -5
- package/src/workers/zcode-gift-claim.ts +108 -0
- package/dist/dashboard/assets/AccountCooldown-Bjo92Qat.js +0 -1
- package/dist/dashboard/assets/AccountCooldown-Bjo92Qat.js.gz +0 -0
- package/dist/dashboard/assets/BannedPage-2TJD37Hb.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-xefJNRmJ.js.gz +0 -0
- package/dist/dashboard/assets/CliToolsPage-Rj9iHQ3F.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-JEbYJFxw.js +0 -1
- package/dist/dashboard/assets/CombosPage-JEbYJFxw.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-C3xx_inM.js +0 -1
- package/dist/dashboard/assets/ConsoleLogPage-C3xx_inM.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-Dw306gJU.js +0 -1
- package/dist/dashboard/assets/CustomizationPage-Dw306gJU.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-jaBo1TWV.js +0 -1
- package/dist/dashboard/assets/HealthEventsModal-jaBo1TWV.js.gz +0 -0
- package/dist/dashboard/assets/LoginPage-C4w7vea-.js.gz +0 -0
- package/dist/dashboard/assets/ModelPicker-BtPcAfot.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-CGC5gNs1.js +0 -1
- package/dist/dashboard/assets/OverviewPage-CGC5gNs1.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-DQcthNGt.js +0 -4
- package/dist/dashboard/assets/ProviderDetailPage-DQcthNGt.js.gz +0 -0
- package/dist/dashboard/assets/ProviderIcon-FB6yS4v1.js +0 -1
- package/dist/dashboard/assets/ProviderIcon-FB6yS4v1.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-0OsXRGXe.js +0 -1
- package/dist/dashboard/assets/ProvidersPage-0OsXRGXe.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-DdPc8slb.js +0 -9
- package/dist/dashboard/assets/ProxyPage-DdPc8slb.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-Ci5O4BnH.js +0 -1
- package/dist/dashboard/assets/QuotaPage-Ci5O4BnH.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-CDZjTt87.js +0 -1
- package/dist/dashboard/assets/SettingsPage-CDZjTt87.js.gz +0 -0
- package/dist/dashboard/assets/SetupPage-BQS0Rx_u.js.gz +0 -0
- package/dist/dashboard/assets/SortableList-BAL5npBC.js +0 -1
- package/dist/dashboard/assets/SortableList-BAL5npBC.js.gz +0 -0
- package/dist/dashboard/assets/StudioPage-TP7voAQ0.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-Cj_fEzhg.js +0 -56
- package/dist/dashboard/assets/UsagePage-Cj_fEzhg.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-MbI7e7rt.js +0 -1
- package/dist/dashboard/assets/api-keys-MbI7e7rt.js.gz +0 -0
- package/dist/dashboard/assets/app-B2vyPoII.css +0 -2
- package/dist/dashboard/assets/app-B2vyPoII.css.gz +0 -0
- package/dist/dashboard/assets/assert-C0YYuZJ4.js.gz +0 -0
- package/dist/dashboard/assets/badge-B_DMpMJN.js.gz +0 -0
- package/dist/dashboard/assets/banner-DMrMPmth.js.gz +0 -0
- package/dist/dashboard/assets/card-Be5Uzamx.js.gz +0 -0
- package/dist/dashboard/assets/cli-tools-muFRphDu.js.gz +0 -0
- package/dist/dashboard/assets/clipboard-button-DOfrVmsi.js.gz +0 -0
- package/dist/dashboard/assets/dialog-C9omdjQt.js.gz +0 -0
- package/dist/dashboard/assets/drawer-Bm8WKaGY.js.gz +0 -0
- package/dist/dashboard/assets/entry-DS90gUks.js +0 -1
- package/dist/dashboard/assets/entry-DS90gUks.js.gz +0 -0
- package/dist/dashboard/assets/entry-j40LJotO.js +0 -4
- package/dist/dashboard/assets/entry-j40LJotO.js.gz +0 -0
- package/dist/dashboard/assets/entry-q5HwZ8nN.js.gz +0 -0
- package/dist/dashboard/assets/formatters-BTScrQpe.js.gz +0 -0
- package/dist/dashboard/assets/github-badge-Orjuzemr.js.gz +0 -0
- package/dist/dashboard/assets/index-cPrX5vn6.js.gz +0 -0
- package/dist/dashboard/assets/inline-Dvux8w-0.js.gz +0 -0
- package/dist/dashboard/assets/input-CU1Rgf3p.js.gz +0 -0
- package/dist/dashboard/assets/list-row-CzFvogbj.js.gz +0 -0
- package/dist/dashboard/assets/live-KaG15HLQ.js +0 -1
- package/dist/dashboard/assets/live-KaG15HLQ.js.gz +0 -0
- package/dist/dashboard/assets/minimize-2-D-MN8W8a.js +0 -1
- package/dist/dashboard/assets/minimize-2-D-MN8W8a.js.gz +0 -0
- package/dist/dashboard/assets/mutation-JJGQm8TD.js.gz +0 -0
- package/dist/dashboard/assets/numbers-BNDZnsUE.js +0 -2
- package/dist/dashboard/assets/numbers-BNDZnsUE.js.gz +0 -0
- package/dist/dashboard/assets/query-keys-CLaNz8Xz.js.gz +0 -0
- package/dist/dashboard/assets/quota-DZGyp0N5.js +0 -1
- package/dist/dashboard/assets/quota-DZGyp0N5.js.gz +0 -0
- package/dist/dashboard/assets/routing-BEH32QOC.js.gz +0 -0
- package/dist/dashboard/assets/select-B3iTetHN.js.gz +0 -0
- package/dist/dashboard/assets/settings-Dt3Eqbnq.js +0 -1
- package/dist/dashboard/assets/settings-Dt3Eqbnq.js.gz +0 -0
- package/dist/dashboard/assets/stack-KrOrm7JZ.js.gz +0 -0
- package/dist/dashboard/assets/state-DLS7IDMy.js.gz +0 -0
- package/dist/dashboard/assets/stream-retry-5QnVuSSo.js +0 -1
- package/dist/dashboard/assets/stream-retry-5QnVuSSo.js.gz +0 -0
- package/dist/dashboard/assets/switch-42ItvbAs.js.gz +0 -0
- package/dist/dashboard/assets/useMutation-Dmnfh1Ue.js.gz +0 -0
- package/src/network/response-headers.ts +0 -39
- package/src/protocol/response/dsml.ts +0 -38
- package/src/providers/integrations/xiaomi-mimo/think-stream.ts +0 -229
- /package/dist/dashboard/assets/{app-wvwCi4Eh.js → app-Bgd3zANQ.js} +0 -0
- /package/dist/dashboard/assets/{app-wvwCi4Eh.js.gz → app-Bgd3zANQ.js.gz} +0 -0
- /package/dist/dashboard/assets/brand/{og_bansos.webp → og_bansos_initial.webp} +0 -0
- /package/dist/dashboard/assets/brand/{og_image.webp → og_image_initial.webp} +0 -0
|
@@ -17,6 +17,10 @@ import { type TelemetryEventRecord } from "../../observability/event-log";
|
|
|
17
17
|
import {
|
|
18
18
|
queryTelemetryEvents as querySqlEvents,
|
|
19
19
|
sumTelemetryTokens,
|
|
20
|
+
familyKpiTotals,
|
|
21
|
+
familyWindowRequests,
|
|
22
|
+
familyHourlyTelemetry,
|
|
23
|
+
familyIpBreakdown,
|
|
20
24
|
} from "../../observability/event-query";
|
|
21
25
|
import type { CartethyiaDatabase } from "../../persistence/db-handle";
|
|
22
26
|
import { apiKeys } from "../../persistence/schema";
|
|
@@ -26,7 +30,7 @@ import { cachedRead } from "../shared/read-through-cache";
|
|
|
26
30
|
import { utcDayStart, utcMonthStart } from "../shared/day";
|
|
27
31
|
|
|
28
32
|
/** Totals for the whole share family, all-time plus the windows the UI draws. */
|
|
29
|
-
|
|
33
|
+
interface ShareFamilyTotals {
|
|
30
34
|
readonly requests: number;
|
|
31
35
|
readonly errors: number;
|
|
32
36
|
readonly inputTokens: number;
|
|
@@ -41,7 +45,7 @@ export interface ShareFamilyTotals {
|
|
|
41
45
|
}
|
|
42
46
|
|
|
43
47
|
/** One hour bucket of the last 24 hours, oldest first. */
|
|
44
|
-
|
|
48
|
+
interface ShareHourlyBucket {
|
|
45
49
|
/** ISO instant at the top of the hour. */
|
|
46
50
|
readonly hour: string;
|
|
47
51
|
readonly requests: number;
|
|
@@ -49,7 +53,7 @@ export interface ShareHourlyBucket {
|
|
|
49
53
|
readonly tokens: number;
|
|
50
54
|
}
|
|
51
55
|
|
|
52
|
-
|
|
56
|
+
interface ShareTopModel {
|
|
53
57
|
readonly modelId: string;
|
|
54
58
|
readonly requests: number;
|
|
55
59
|
readonly tokens: number;
|
|
@@ -59,7 +63,7 @@ export interface ShareTopModel {
|
|
|
59
63
|
readonly avgTtfbMs: number | null;
|
|
60
64
|
}
|
|
61
65
|
|
|
62
|
-
|
|
66
|
+
interface ShareTopClientIp {
|
|
63
67
|
/** Always masked: this payload is served to the recipient, not the tenant. */
|
|
64
68
|
readonly ip: string;
|
|
65
69
|
readonly requests: number;
|
|
@@ -72,6 +76,12 @@ export interface ShareTopClientIp {
|
|
|
72
76
|
* the tool rather than the machine.
|
|
73
77
|
*/
|
|
74
78
|
readonly clientType: string | null;
|
|
79
|
+
/**
|
|
80
|
+
* True when this address made a request within {@link ACTIVE_IP_WINDOW_MS}.
|
|
81
|
+
* A recipient watching the link can tell which addresses are still using it
|
|
82
|
+
* versus ones that stopped days ago.
|
|
83
|
+
*/
|
|
84
|
+
readonly active: boolean;
|
|
75
85
|
}
|
|
76
86
|
|
|
77
87
|
export interface ShareFamilyStats {
|
|
@@ -111,6 +121,24 @@ export interface ShareStatsPort {
|
|
|
111
121
|
|
|
112
122
|
const HOUR_MS = 3_600_000;
|
|
113
123
|
|
|
124
|
+
/**
|
|
125
|
+
* How old an address's last request may be and still count as active on the
|
|
126
|
+
* share page. 24 hours matches the page's own "last 24 hours" framing: an IP
|
|
127
|
+
* that made a request since yesterday is "active", one that stopped before
|
|
128
|
+
* that window is "inactive".
|
|
129
|
+
*/
|
|
130
|
+
const ACTIVE_IP_WINDOW_MS = 24 * HOUR_MS;
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Recency window for the client-IP ranking's *work set*: the GROUP BY only
|
|
134
|
+
* scans this slice of telemetry instead of the whole retained window every
|
|
135
|
+
* tick. Within the window, addresses are ranked by request volume (highest
|
|
136
|
+
* first), and older addresses fall out of the ranking entirely. 7 days keeps
|
|
137
|
+
* the ranking useful while staying orders of magnitude smaller than the
|
|
138
|
+
* retained window.
|
|
139
|
+
*/
|
|
140
|
+
const IP_WINDOW_MS = 7 * 24 * HOUR_MS;
|
|
141
|
+
|
|
114
142
|
/**
|
|
115
143
|
* Names the client behind a request from its User-Agent.
|
|
116
144
|
*
|
|
@@ -154,7 +182,7 @@ interface FamilyRollup {
|
|
|
154
182
|
totals: ShareFamilyTotals;
|
|
155
183
|
byHour: Map<string, { requests: number; tokens: number }>;
|
|
156
184
|
models: Map<string, { modelId: string; requests: number; tokens: number; rateSum: number; rateCount: number; ttfbSum: number; ttfbCount: number }>;
|
|
157
|
-
ips: Map<string, { ip: string; requests: number; tokens: number; lastSeenAt: string | null; userAgent: string | null }>;
|
|
185
|
+
ips: Map<string, { ip: string; requests: number; tokens: number; lastSeenAt: string | null; userAgent: string | null; active: boolean }>;
|
|
158
186
|
}
|
|
159
187
|
|
|
160
188
|
/**
|
|
@@ -163,7 +191,7 @@ interface FamilyRollup {
|
|
|
163
191
|
*/
|
|
164
192
|
function rollupFamily(
|
|
165
193
|
events: readonly TelemetryEventRecord[],
|
|
166
|
-
bounds: { dayStart: number; monthStart: number; lastHourStart: number; windowStart: number; allowed: ReadonlySet<string> | null },
|
|
194
|
+
bounds: { dayStart: number; monthStart: number; lastHourStart: number; windowStart: number; activeIpCutoff: number; allowed: ReadonlySet<string> | null },
|
|
167
195
|
): FamilyRollup {
|
|
168
196
|
const totals = { requests: 0, errors: 0, inputTokens: 0, outputTokens: 0, totalTokens: 0, lastHourRequests: 0, todayTokens: 0, monthTokens: 0 };
|
|
169
197
|
const byHour = new Map<string, { requests: number; tokens: number }>();
|
|
@@ -196,13 +224,14 @@ function rollupFamily(
|
|
|
196
224
|
models.set(event.requestedModel, model);
|
|
197
225
|
}
|
|
198
226
|
if (event.clientIp !== null) {
|
|
199
|
-
const ip = ips.get(event.clientIp) ?? { ip: event.clientIp, requests: 0, tokens: 0, lastSeenAt: null, userAgent: null };
|
|
227
|
+
const ip = ips.get(event.clientIp) ?? { ip: event.clientIp, requests: 0, tokens: 0, lastSeenAt: null, userAgent: null, active: false };
|
|
200
228
|
ip.requests += 1;
|
|
201
229
|
ip.tokens += tokens;
|
|
202
230
|
// Events arrive newest first, so the first one seen is the most recent.
|
|
203
231
|
if (ip.lastSeenAt === null) {
|
|
204
232
|
ip.lastSeenAt = event.createdAt;
|
|
205
233
|
ip.userAgent = event.userAgent;
|
|
234
|
+
ip.active = at >= bounds.activeIpCutoff;
|
|
206
235
|
}
|
|
207
236
|
ips.set(event.clientIp, ip);
|
|
208
237
|
}
|
|
@@ -244,17 +273,18 @@ const HOURS_WINDOW = 24;
|
|
|
244
273
|
/**
|
|
245
274
|
* Cache lifetime for one family's rollup.
|
|
246
275
|
*
|
|
247
|
-
*
|
|
248
|
-
*
|
|
249
|
-
*
|
|
250
|
-
*
|
|
251
|
-
*
|
|
276
|
+
* Longer than the SSE tick (30s) so most ticks are served from cache. The
|
|
277
|
+
* cold-compute path reads the family's telemetry windows (24h chart, 7d IP
|
|
278
|
+
* ranking), which on a busy family is the single most expensive thing the
|
|
279
|
+
* share page does; a 5-minute TTL caps that to once per family per 5
|
|
280
|
+
* minutes while the page still reads as live — KPI numbers move with
|
|
281
|
+
* traffic, not on a per-minute cadence.
|
|
252
282
|
*
|
|
253
283
|
* Declared here rather than imported from the router so this port does not
|
|
254
|
-
* depend on it. `SHARE_STATS_STREAM_INTERVAL_MS` must
|
|
255
|
-
*
|
|
284
|
+
* depend on it. `SHARE_STATS_STREAM_INTERVAL_MS` must stay below it, or the
|
|
285
|
+
* tick and cache drift apart and every tick bypasses the cache.
|
|
256
286
|
*/
|
|
257
|
-
export const SHARE_STATS_CACHE_TTL_SECONDS =
|
|
287
|
+
export const SHARE_STATS_CACHE_TTL_SECONDS = 300;
|
|
258
288
|
|
|
259
289
|
/**
|
|
260
290
|
* Cache key for one family's rollup. The family is named by its template key
|
|
@@ -318,17 +348,23 @@ async function computeFamilyStatsBody(
|
|
|
318
348
|
const hourStart = utcHourStart(now);
|
|
319
349
|
const lastHourStart = new Date(now.getTime() - HOUR_MS);
|
|
320
350
|
const windowStart = new Date(hourStart.getTime() - (HOURS_WINDOW - 1) * HOUR_MS);
|
|
351
|
+
// The IP breakdown is bounded to this recency window: a family's "who is
|
|
352
|
+
// using the link" answer only needs addresses that were active recently,
|
|
353
|
+
// and an unbounded GROUP BY over every retained event is the one remaining
|
|
354
|
+
// full-window scan on the share page.
|
|
355
|
+
const ipWindowStart = new Date(now.getTime() - IP_WINDOW_MS);
|
|
321
356
|
|
|
322
|
-
// All-time
|
|
323
|
-
//
|
|
324
|
-
// the
|
|
325
|
-
//
|
|
326
|
-
//
|
|
327
|
-
//
|
|
328
|
-
const [
|
|
329
|
-
|
|
357
|
+
// All-time family KPI comes from the durable rollup
|
|
358
|
+
// (`telemetry_usage_totals`, charged at write time), not from re-aggregating
|
|
359
|
+
// the entire retained telemetry window on every tick: the share page's tick
|
|
360
|
+
// would otherwise read every event a busy family ever produced just to draw
|
|
361
|
+
// four numbers. Today's/month's spend and the last-hour count are bounded
|
|
362
|
+
// window reads over the time index, which stay small.
|
|
363
|
+
const [kpi, todayTokens, monthTokens, lastHourRequests, template] = await Promise.all([
|
|
364
|
+
familyKpiTotals(db, tenantId, templateKeyId),
|
|
330
365
|
sumTelemetryTokens(db, tenantId, { familyKeyId: templateKeyId, since: dayStart }),
|
|
331
366
|
sumTelemetryTokens(db, tenantId, { familyKeyId: templateKeyId, since: monthStart }),
|
|
367
|
+
familyWindowRequests(db, tenantId, templateKeyId, lastHourStart),
|
|
332
368
|
db
|
|
333
369
|
.select({ lifetime: apiKeys.lifetimeTokensConsumed })
|
|
334
370
|
.from(apiKeys)
|
|
@@ -342,8 +378,9 @@ async function computeFamilyStatsBody(
|
|
|
342
378
|
// across the overview, the share dialog, and this page.
|
|
343
379
|
const templateLifetime = Number(template[0]?.lifetime ?? 0);
|
|
344
380
|
|
|
345
|
-
// The
|
|
346
|
-
//
|
|
381
|
+
// The ranked tables need real events, bounded to the 24-hour window. The
|
|
382
|
+
// totals and hourly chart are computed in SQL, so the page cap here only
|
|
383
|
+
// shapes the model/IP rankings — never the KPI numbers.
|
|
347
384
|
const page = await querySqlEvents(db, {
|
|
348
385
|
tenantId,
|
|
349
386
|
familyApiKeyId: templateKeyId,
|
|
@@ -356,6 +393,7 @@ async function computeFamilyStatsBody(
|
|
|
356
393
|
monthStart: monthStart.getTime(),
|
|
357
394
|
lastHourStart: lastHourStart.getTime(),
|
|
358
395
|
windowStart: windowStart.getTime(),
|
|
396
|
+
activeIpCutoff: now.getTime() - ACTIVE_IP_WINDOW_MS,
|
|
359
397
|
allowed: allowedSet,
|
|
360
398
|
});
|
|
361
399
|
rollup.totals = {
|
|
@@ -363,14 +401,28 @@ async function computeFamilyStatsBody(
|
|
|
363
401
|
// The template counter is the seed on the running total, not a distinct
|
|
364
402
|
// bucket: daily/monthly windows are telemetry-only (the counter has no
|
|
365
403
|
// time axis), so those keep their own sums.
|
|
366
|
-
totalTokens: templateLifetime +
|
|
404
|
+
totalTokens: templateLifetime + kpi.tokens,
|
|
367
405
|
todayTokens,
|
|
368
406
|
monthTokens,
|
|
407
|
+
// Lifetime counts come from the durable rollup, not the page: a family
|
|
408
|
+
// busier than the page cap must not freeze at 500 requests/errors.
|
|
409
|
+
requests: kpi.requests,
|
|
410
|
+
errors: kpi.errors,
|
|
411
|
+
lastHourRequests,
|
|
369
412
|
};
|
|
370
413
|
|
|
371
|
-
//
|
|
372
|
-
//
|
|
373
|
-
|
|
414
|
+
// The hourly chart is a SQL aggregate over the whole family window, so a
|
|
415
|
+
// busy family's bars stay true. Buckets are filled in JS because only hours
|
|
416
|
+
// that saw traffic are present; a chart drawn from sparse rows would
|
|
417
|
+
// silently compress the axis.
|
|
418
|
+
const bucketSeconds = HOUR_MS / 1000;
|
|
419
|
+
const sqlHourly = await familyHourlyTelemetry(db, tenantId, templateKeyId, windowStart, bucketSeconds);
|
|
420
|
+
const byHour = new Map(
|
|
421
|
+
sqlHourly.map((bucket) => [
|
|
422
|
+
new Date(bucket.at).toISOString(),
|
|
423
|
+
{ requests: bucket.requests, tokens: bucket.inputTokens + bucket.outputTokens },
|
|
424
|
+
]),
|
|
425
|
+
);
|
|
374
426
|
const hourly: ShareHourlyBucket[] = [];
|
|
375
427
|
for (let index = 0; index < HOURS_WINDOW; index += 1) {
|
|
376
428
|
const hour = new Date(windowStart.getTime() + index * HOUR_MS).toISOString();
|
|
@@ -378,6 +430,33 @@ async function computeFamilyStatsBody(
|
|
|
378
430
|
hourly.push({ hour, requests: bucket?.requests ?? 0, tokens: bucket?.tokens ?? 0 });
|
|
379
431
|
}
|
|
380
432
|
|
|
433
|
+
// Client IPs come from a SQL aggregate over a bounded recent window, ranked
|
|
434
|
+
// by request volume — so a family's "who is using the link most" answer
|
|
435
|
+
// stays cheap instead of scanning the whole retained telemetry on every
|
|
436
|
+
// tick. `clientType` is best-effort from the page's events (the most recent
|
|
437
|
+
// 24 hours), which carry the User-Agent.
|
|
438
|
+
const ipRows = await familyIpBreakdown(db, tenantId, templateKeyId, TOP_IPS_LIMIT, ipWindowStart);
|
|
439
|
+
const clientTypeByIp = new Map<string, string | null>();
|
|
440
|
+
for (const event of events) {
|
|
441
|
+
if (event.clientIp !== null && !clientTypeByIp.has(event.clientIp)) {
|
|
442
|
+
clientTypeByIp.set(event.clientIp, clientTypeFromUserAgent(event.userAgent));
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
const activeIpCutoff = now.getTime() - ACTIVE_IP_WINDOW_MS;
|
|
446
|
+
const clientIps = ipRows.map((row) => {
|
|
447
|
+
const lastSeenAt =
|
|
448
|
+
row.lastSeenAt instanceof Date ? row.lastSeenAt : new Date(String(row.lastSeenAt ?? ""));
|
|
449
|
+
const lastSeenMs = Number.isFinite(lastSeenAt.getTime()) ? lastSeenAt.getTime() : 0;
|
|
450
|
+
return {
|
|
451
|
+
ip: maskClientIp(row.ip),
|
|
452
|
+
requests: row.requests,
|
|
453
|
+
tokens: row.tokens,
|
|
454
|
+
lastSeenAt: lastSeenMs > 0 ? lastSeenAt.toISOString() : null,
|
|
455
|
+
clientType: clientTypeByIp.get(row.ip) ?? null,
|
|
456
|
+
active: lastSeenMs >= activeIpCutoff,
|
|
457
|
+
};
|
|
458
|
+
});
|
|
459
|
+
|
|
381
460
|
return {
|
|
382
461
|
totals: rollup.totals,
|
|
383
462
|
hourly,
|
|
@@ -391,15 +470,6 @@ async function computeFamilyStatsBody(
|
|
|
391
470
|
avgTokensPerSec: row.rateCount > 0 ? row.rateSum / row.rateCount : null,
|
|
392
471
|
avgTtfbMs: row.ttfbCount > 0 ? Math.round(row.ttfbSum / row.ttfbCount) : null,
|
|
393
472
|
})),
|
|
394
|
-
clientIps
|
|
395
|
-
.sort((a, b) => b.requests - a.requests)
|
|
396
|
-
.slice(0, TOP_IPS_LIMIT)
|
|
397
|
-
.map((row) => ({
|
|
398
|
-
ip: maskClientIp(row.ip),
|
|
399
|
-
requests: row.requests,
|
|
400
|
-
tokens: row.tokens,
|
|
401
|
-
lastSeenAt: row.lastSeenAt,
|
|
402
|
-
clientType: clientTypeFromUserAgent(row.userAgent),
|
|
403
|
-
})),
|
|
473
|
+
clientIps,
|
|
404
474
|
};
|
|
405
475
|
}
|
|
@@ -13,7 +13,7 @@ import { apiKeys, telemetryUsageTotals } from "../../persistence/schema";
|
|
|
13
13
|
import { shouldMaskClientIp } from "../shared/ip-privacy";
|
|
14
14
|
import { utcDayStart } from "../shared/day";
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
interface SharedKeyTokenUsage {
|
|
17
17
|
readonly requests: number;
|
|
18
18
|
readonly errors: number;
|
|
19
19
|
readonly inputTokens: number;
|
|
@@ -7,7 +7,7 @@ import { formatPublicErrorMessage } from "../../protocol/gateway-error";
|
|
|
7
7
|
// (or the storage-layer BackupError, same code/status/message shape) and
|
|
8
8
|
// shapes it through errorResponse; guards narrow to TenantAccessDecision.
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
interface ErrorResponseOptions {
|
|
11
11
|
readonly detailsPolicy?: "omit" | "include-if-present" | "always-include";
|
|
12
12
|
}
|
|
13
13
|
|
|
@@ -105,7 +105,7 @@ export function requireAnyScope(
|
|
|
105
105
|
return access;
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
|
|
108
|
+
type TenantAccessDecision = AccessDecision & { readonly tenantId: string };
|
|
109
109
|
|
|
110
110
|
export function requireTenantScope(
|
|
111
111
|
access: AccessDecision | undefined,
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Builds an object from `source`, omitting absent values.
|
|
3
|
+
*
|
|
4
|
+
* The console domains hand-roll the same `...(x === undefined ? {} : { x })`
|
|
5
|
+
* field-omission spread many times (key create/update patches, response
|
|
6
|
+
* sanitizers). This is the single helper for that pattern; `omitNull` extends
|
|
7
|
+
* it to also drop `null` values (the `== null` variant), matching call sites
|
|
8
|
+
* that treat null as "clear this field" rather than "set it to null".
|
|
9
|
+
*/
|
|
10
|
+
export function pickDefined<T extends object>(source: T, omitNull = false): { [K in keyof T]?: Exclude<T[K], undefined> } {
|
|
11
|
+
const out: Record<string, unknown> = {};
|
|
12
|
+
for (const [key, value] of Object.entries(source)) {
|
|
13
|
+
if (value === undefined) continue;
|
|
14
|
+
if (omitNull && value === null) continue;
|
|
15
|
+
out[key] = value;
|
|
16
|
+
}
|
|
17
|
+
return out as { [K in keyof T]?: Exclude<T[K], undefined> };
|
|
18
|
+
}
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
import { createHash } from "node:crypto";
|
|
32
32
|
|
|
33
33
|
/** The slice of Redis this cache needs, so it can be tested without a server. */
|
|
34
|
-
|
|
34
|
+
interface ReadThroughCacheClient {
|
|
35
35
|
get(key: string): Promise<string | null>;
|
|
36
36
|
set(key: string, value: string, mode: "EX", ttlSeconds: number): Promise<unknown>;
|
|
37
37
|
}
|
|
@@ -41,7 +41,7 @@ export interface ReadThroughCacheClient {
|
|
|
41
41
|
* more often pass a shorter TTL; this value suits the dashboard's polling
|
|
42
42
|
* cadence and the append-only telemetry log.
|
|
43
43
|
*/
|
|
44
|
-
|
|
44
|
+
const READ_THROUGH_DEFAULT_TTL_SECONDS = 10;
|
|
45
45
|
|
|
46
46
|
/** The cached value plus the strong validator for the bytes it serializes to. */
|
|
47
47
|
export interface CachedResult<T> {
|
|
@@ -59,10 +59,38 @@ function etagFor(bytes: string): string {
|
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
/** The sibling key holding the validator, so a cached body is never re-hashed. */
|
|
62
|
-
|
|
62
|
+
function etagKey(key: string): string {
|
|
63
63
|
return `${key}:etag`;
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
+
/**
|
|
67
|
+
* In-memory fallback store used when no Redis client is configured (the
|
|
68
|
+
* embedded lite backend). Keyed by the same cache key, storing the value and
|
|
69
|
+
* its validator with a TTL. Bounded so a busy gateway cannot grow it without
|
|
70
|
+
* limit.
|
|
71
|
+
*/
|
|
72
|
+
const LOCAL_CACHE_MAX_ENTRIES = 2000;
|
|
73
|
+
const localCache = new Map<string, { at: number; value: string; etag: string }>();
|
|
74
|
+
|
|
75
|
+
function localGet(key: string): { value: string; etag: string } | undefined {
|
|
76
|
+
const entry = localCache.get(key);
|
|
77
|
+
if (entry === undefined) return undefined;
|
|
78
|
+
if (Date.now() - entry.at >= READ_THROUGH_DEFAULT_TTL_SECONDS * 1000) {
|
|
79
|
+
localCache.delete(key);
|
|
80
|
+
return undefined;
|
|
81
|
+
}
|
|
82
|
+
return { value: entry.value, etag: entry.etag };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function localSet(key: string, value: string, etag: string): void {
|
|
86
|
+
if (localCache.size >= LOCAL_CACHE_MAX_ENTRIES) {
|
|
87
|
+
// Evict the oldest entry (Map iterates in insertion order).
|
|
88
|
+
const oldest = localCache.keys().next();
|
|
89
|
+
if (!oldest.done) localCache.delete(oldest.value);
|
|
90
|
+
}
|
|
91
|
+
localCache.set(key, { at: Date.now(), value, etag });
|
|
92
|
+
}
|
|
93
|
+
|
|
66
94
|
/**
|
|
67
95
|
* Reads through the cache and returns a strong validator alongside the value.
|
|
68
96
|
*
|
|
@@ -74,7 +102,8 @@ export function etagKey(key: string): string {
|
|
|
74
102
|
* With no cache configured (`client === undefined`) the value is computed and
|
|
75
103
|
* the validator is still returned, so conditional requests keep working for
|
|
76
104
|
* the length of the client's own polling loop; only the cross-instance sharing
|
|
77
|
-
* is lost.
|
|
105
|
+
* is lost. A process-local Map serves the same TTL when Redis is absent, so a
|
|
106
|
+
* poller still gets cache hits.
|
|
78
107
|
*/
|
|
79
108
|
export async function cachedReadWithEtag<T>(
|
|
80
109
|
client: ReadThroughCacheClient | undefined,
|
|
@@ -83,8 +112,15 @@ export async function cachedReadWithEtag<T>(
|
|
|
83
112
|
ttlSeconds: number = READ_THROUGH_DEFAULT_TTL_SECONDS,
|
|
84
113
|
): Promise<CachedResult<T>> {
|
|
85
114
|
if (client === undefined) {
|
|
115
|
+
const entry = localGet(key);
|
|
116
|
+
if (entry !== undefined) {
|
|
117
|
+
return { value: JSON.parse(entry.value) as T, etag: entry.etag };
|
|
118
|
+
}
|
|
86
119
|
const fresh = await compute();
|
|
87
|
-
|
|
120
|
+
const bytes = JSON.stringify(fresh) ?? "null";
|
|
121
|
+
const etag = etagFor(bytes);
|
|
122
|
+
localSet(key, bytes, etag);
|
|
123
|
+
return { value: fresh, etag };
|
|
88
124
|
}
|
|
89
125
|
try {
|
|
90
126
|
const hit = await client.get(key);
|
|
@@ -160,7 +196,7 @@ export function conditionalResponse<T>(
|
|
|
160
196
|
}
|
|
161
197
|
|
|
162
198
|
/** True when an `If-None-Match` header names this validator. */
|
|
163
|
-
|
|
199
|
+
function matchesEtag(header: string | null, etag: string): boolean {
|
|
164
200
|
if (header === null) return false;
|
|
165
201
|
const trimmed = header.trim();
|
|
166
202
|
if (trimmed === "*") return true;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { CartethyiaDatabase } from "../../persistence/db-handle";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Casts a Drizzle nested-transaction handle to the shared database type.
|
|
5
|
+
*
|
|
6
|
+
* Drizzle types a nested transaction callback with the dialect's own
|
|
7
|
+
* transaction handle, which differs from `CartethyiaDatabase`. The cast is
|
|
8
|
+
* required (both drivers implement the surface stores use), but it is a type
|
|
9
|
+
* hole every time it appears raw — so it is centralized here rather than
|
|
10
|
+
* scattered across call sites.
|
|
11
|
+
*/
|
|
12
|
+
export function asConsoleDb(tx: unknown): CartethyiaDatabase {
|
|
13
|
+
return tx as unknown as CartethyiaDatabase;
|
|
14
|
+
}
|
|
@@ -16,33 +16,70 @@ export interface PromptSetting {
|
|
|
16
16
|
export const PROMPT_MODES = ["append", "override"] as const;
|
|
17
17
|
export type PromptMode = (typeof PROMPT_MODES)[number];
|
|
18
18
|
|
|
19
|
-
/**
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
19
|
+
/**
|
|
20
|
+
* A named, reusable prompt. Scopes reference presets by id instead of each
|
|
21
|
+
* carrying its own text, so one tone/ruleset can be applied to many scopes.
|
|
22
|
+
*/
|
|
23
|
+
export interface SystemPromptPreset {
|
|
24
|
+
readonly id: string;
|
|
25
|
+
readonly name: string;
|
|
26
|
+
readonly enabled: boolean;
|
|
27
|
+
readonly mode: PromptMode;
|
|
28
|
+
readonly text: string;
|
|
24
29
|
}
|
|
25
30
|
|
|
31
|
+
/**
|
|
32
|
+
* How one scope decides what to inject. Modes:
|
|
33
|
+
*
|
|
34
|
+
* - `off` — explicitly inject nothing for this scope; scope-specific stop,
|
|
35
|
+
* does NOT fall through to the global prompt.
|
|
36
|
+
* - `global` — follow the tenant's global prompt (the default posture).
|
|
37
|
+
* - `preset` — use the referenced named preset (disabled preset = no inject).
|
|
38
|
+
* - `custom` — carry this scope's own prompt inline.
|
|
39
|
+
*
|
|
40
|
+
* A scope with no entry at all behaves as `global` (resolution falls through
|
|
41
|
+
* to the global prompt), so omitting a key is the same as an explicit
|
|
42
|
+
* `{ kind: "global" }` reference. The global scope itself never uses
|
|
43
|
+
* `{ kind: "global" }` — it is `null` (off), a preset, or a custom setting.
|
|
44
|
+
*/
|
|
45
|
+
export type ScopePromptRef =
|
|
46
|
+
| { readonly kind: "off" }
|
|
47
|
+
| { readonly kind: "global" }
|
|
48
|
+
| { readonly kind: "preset"; readonly presetId: string }
|
|
49
|
+
| { readonly kind: "custom"; readonly setting: PromptSetting };
|
|
50
|
+
|
|
26
51
|
/** The full per-tenant configuration, one row in `tenant_system_prompts`. */
|
|
27
52
|
export interface TenantSystemPrompts {
|
|
28
|
-
/** `null` =
|
|
29
|
-
readonly global:
|
|
30
|
-
/**
|
|
31
|
-
readonly
|
|
32
|
-
/**
|
|
33
|
-
readonly
|
|
53
|
+
/** `null` = no global injection — distinct from an explicit disabled setting. */
|
|
54
|
+
readonly global: ScopePromptRef | null;
|
|
55
|
+
/** preset id → preset */
|
|
56
|
+
readonly presets: Readonly<Record<string, SystemPromptPreset>>;
|
|
57
|
+
/** providerId → ref */
|
|
58
|
+
readonly providers: Readonly<Record<string, ScopePromptRef>>;
|
|
59
|
+
/** `${providerId}::${modelId}` → ref */
|
|
60
|
+
readonly models: Readonly<Record<string, ScopePromptRef>>;
|
|
61
|
+
/** combo name → ref */
|
|
62
|
+
readonly combos: Readonly<Record<string, ScopePromptRef>>;
|
|
63
|
+
/** alias name → ref */
|
|
64
|
+
readonly aliases: Readonly<Record<string, ScopePromptRef>>;
|
|
34
65
|
}
|
|
35
66
|
|
|
36
67
|
/** Empty configuration: nothing configured anywhere. */
|
|
37
68
|
export const EMPTY_TENANT_SYSTEM_PROMPTS: TenantSystemPrompts = {
|
|
38
69
|
global: null,
|
|
70
|
+
presets: {},
|
|
39
71
|
providers: {},
|
|
40
72
|
models: {},
|
|
73
|
+
combos: {},
|
|
74
|
+
aliases: {},
|
|
41
75
|
};
|
|
42
76
|
|
|
43
77
|
/** Maximum stored text per scope (5 MB). Enforced at the console API. */
|
|
44
78
|
export const SYSTEM_PROMPT_TEXT_CAP_BYTES = 5_000_000;
|
|
45
79
|
|
|
80
|
+
/** Maximum stored preset-name length. Enforced at the console API. */
|
|
81
|
+
export const SYSTEM_PROMPT_PRESET_NAME_CAP = 200;
|
|
82
|
+
|
|
46
83
|
/**
|
|
47
84
|
* Marker wrapping tenant text injected into a provider wire whose upstream
|
|
48
85
|
* validates the system prompt as a channel identity (the buddy family). The
|
|
@@ -52,12 +89,21 @@ export const SYSTEM_PROMPT_TEXT_CAP_BYTES = 5_000_000;
|
|
|
52
89
|
export const SYSTEM_PROMPT_WRAPPER_PREFIX = "<cartethyia:system-prompt>";
|
|
53
90
|
export const SYSTEM_PROMPT_WRAPPER_SUFFIX = "</cartethyia:system-prompt>";
|
|
54
91
|
|
|
92
|
+
/** Maximum number of presets stored for one tenant. */
|
|
93
|
+
export const SYSTEM_PROMPT_PRESET_LIMIT = 64;
|
|
94
|
+
|
|
55
95
|
/** Maximum number of per-provider entries stored for one tenant. */
|
|
56
96
|
export const SYSTEM_PROMPT_PROVIDER_LIMIT = 256;
|
|
57
97
|
|
|
58
98
|
/** Maximum number of per-model entries stored for one tenant. */
|
|
59
99
|
export const SYSTEM_PROMPT_MODEL_LIMIT = 512;
|
|
60
100
|
|
|
101
|
+
/** Maximum number of per-combo entries stored for one tenant. */
|
|
102
|
+
export const SYSTEM_PROMPT_COMBO_LIMIT = 512;
|
|
103
|
+
|
|
104
|
+
/** Maximum number of per-alias entries stored for one tenant. */
|
|
105
|
+
export const SYSTEM_PROMPT_ALIAS_LIMIT = 512;
|
|
106
|
+
|
|
61
107
|
/**
|
|
62
108
|
* Providers whose upstream validates the fixed system prompt as a channel
|
|
63
109
|
* identity. Their system turn must keep the fixed prompt as the opener, so
|
|
@@ -66,7 +112,7 @@ export const SYSTEM_PROMPT_MODEL_LIMIT = 512;
|
|
|
66
112
|
* `override` mode is not honoured for these providers — it would strip the
|
|
67
113
|
* identity and the upstream would refuse the request.
|
|
68
114
|
*/
|
|
69
|
-
|
|
115
|
+
const WRAPPED_INJECTION_PROVIDERS: ReadonlySet<string> = new Set([
|
|
70
116
|
"cb", // CodeBuddy International
|
|
71
117
|
"cbcn", // CodeBuddy CN
|
|
72
118
|
"workbuddy",
|
|
@@ -100,3 +146,34 @@ export function isPromptSetting(value: unknown): value is PromptSetting {
|
|
|
100
146
|
typeof candidate.text === "string"
|
|
101
147
|
);
|
|
102
148
|
}
|
|
149
|
+
|
|
150
|
+
/** True when the value is a well-formed preset (shape only, not size). */
|
|
151
|
+
export function isSystemPromptPreset(value: unknown): value is SystemPromptPreset {
|
|
152
|
+
if (typeof value !== "object" || value === null) return false;
|
|
153
|
+
const candidate = value as Partial<SystemPromptPreset>;
|
|
154
|
+
return (
|
|
155
|
+
typeof candidate.id === "string" &&
|
|
156
|
+
candidate.id.length > 0 &&
|
|
157
|
+
typeof candidate.name === "string" &&
|
|
158
|
+
candidate.name.length > 0 &&
|
|
159
|
+
typeof candidate.enabled === "boolean" &&
|
|
160
|
+
typeof candidate.mode === "string" &&
|
|
161
|
+
(PROMPT_MODES as readonly string[]).includes(candidate.mode) &&
|
|
162
|
+
typeof candidate.text === "string"
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/** True when the value is a well-formed scope reference (shape only). */
|
|
167
|
+
export function isScopePromptRef(value: unknown): value is ScopePromptRef {
|
|
168
|
+
if (typeof value !== "object" || value === null) return false;
|
|
169
|
+
const kind = (value as { kind?: unknown }).kind;
|
|
170
|
+
if (kind === "off" || kind === "global") return true;
|
|
171
|
+
if (kind === "preset") {
|
|
172
|
+
const presetId = (value as { presetId?: unknown }).presetId;
|
|
173
|
+
return typeof presetId === "string" && presetId.length > 0;
|
|
174
|
+
}
|
|
175
|
+
if (kind === "custom") {
|
|
176
|
+
return isPromptSetting((value as { setting?: unknown }).setting);
|
|
177
|
+
}
|
|
178
|
+
return false;
|
|
179
|
+
}
|