cartethyia 2.7.4 → 2.7.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +2 -0
- package/README.md +17 -5
- package/dist/dashboard/assets/{BannedPage-ChG5svFb.js → BannedPage-Cj15GsMp.js} +1 -1
- package/dist/dashboard/assets/BannedPage-Cj15GsMp.js.gz +0 -0
- package/dist/dashboard/assets/ClaimGiftsButton-D83pBJ6J.js +1 -0
- package/dist/dashboard/assets/ClaimGiftsButton-D83pBJ6J.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolDetailPage-BIcWQun7.js → CliToolDetailPage-DZKmVuJN.js} +1 -1
- package/dist/dashboard/assets/CliToolDetailPage-DZKmVuJN.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolsPage-Bf2VelDe.js → CliToolsPage-BIccZmeL.js} +1 -1
- package/dist/dashboard/assets/CliToolsPage-BIccZmeL.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-DEEKkYD-.js +1 -0
- package/dist/dashboard/assets/CombosPage-DEEKkYD-.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-D5rol2j2.js +1 -0
- package/dist/dashboard/assets/ConsoleLogPage-D5rol2j2.js.gz +0 -0
- package/dist/dashboard/assets/{CustomizationPage-ggeMSjte.js → CustomizationPage-BKj_r0rp.js} +1 -1
- package/dist/dashboard/assets/CustomizationPage-BKj_r0rp.js.gz +0 -0
- package/dist/dashboard/assets/InjectSystemPromptPanel-B5nZvmf0.js +656 -0
- package/dist/dashboard/assets/InjectSystemPromptPanel-B5nZvmf0.js.gz +0 -0
- package/dist/dashboard/assets/{LoginPage-U6AKaJcF.js → LoginPage-DkXb6dx-.js} +1 -1
- package/dist/dashboard/assets/LoginPage-DkXb6dx-.js.gz +0 -0
- package/dist/dashboard/assets/{ModelPicker-BMS2t8BA.js → ModelPicker-DnQPSIA8.js} +1 -1
- package/dist/dashboard/assets/ModelPicker-DnQPSIA8.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-Ci2wQKUz.js +1 -0
- package/dist/dashboard/assets/OverviewPage-Ci2wQKUz.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-odlgYrOg.js +5 -0
- package/dist/dashboard/assets/ProviderDetailPage-odlgYrOg.js.gz +0 -0
- package/dist/dashboard/assets/ProviderIcon-EfeloBn4.js +1 -0
- package/dist/dashboard/assets/ProviderIcon-EfeloBn4.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-CqFBQEB8.js +1 -0
- package/dist/dashboard/assets/ProvidersPage-CqFBQEB8.js.gz +0 -0
- package/dist/dashboard/assets/{ProxyPage-lsrx7NYg.js → ProxyPage-5SnUSy3j.js} +1 -1
- package/dist/dashboard/assets/ProxyPage-5SnUSy3j.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-CmnMDzhP.js +1 -0
- package/dist/dashboard/assets/QuotaPage-CmnMDzhP.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-BJtRZ4qD.js +1 -0
- package/dist/dashboard/assets/SettingsPage-BJtRZ4qD.js.gz +0 -0
- package/dist/dashboard/assets/{SetupPage-f0DE1h0-.js → SetupPage-WTa0sktC.js} +1 -1
- package/dist/dashboard/assets/SetupPage-WTa0sktC.js.gz +0 -0
- package/dist/dashboard/assets/{StudioPage-Cq5tW7hW.js → StudioPage-BfAb5-o_.js} +3 -3
- package/dist/dashboard/assets/StudioPage-BfAb5-o_.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-BNzeZ2Vk.js +56 -0
- package/dist/dashboard/assets/UsagePage-BNzeZ2Vk.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-l29JROPv.js +1 -0
- package/dist/dashboard/assets/api-keys-l29JROPv.js.gz +0 -0
- package/dist/dashboard/assets/app--PJWq8_C.css +2 -0
- package/dist/dashboard/assets/app--PJWq8_C.css.gz +0 -0
- package/dist/dashboard/assets/assert-C0K-c4eo.js +1 -0
- package/dist/dashboard/assets/assert-C0K-c4eo.js.gz +0 -0
- package/dist/dashboard/assets/{badge-D9CS6dZk.js → badge-DfbhQU4k.js} +1 -1
- package/dist/dashboard/assets/badge-DfbhQU4k.js.gz +0 -0
- package/dist/dashboard/assets/{banner-DympBNq4.js → banner-BllT4LcF.js} +1 -1
- package/dist/dashboard/assets/banner-BllT4LcF.js.gz +0 -0
- package/dist/dashboard/assets/brand/login_register.jpg +0 -0
- package/dist/dashboard/assets/brand/login_register_initial.jpg +0 -0
- package/dist/dashboard/assets/brand/og_bansos.jpg +0 -0
- package/dist/dashboard/assets/brand/og_image.jpg +0 -0
- package/dist/dashboard/assets/{card-ibRWU_uT.js → card-CtkX66Wd.js} +1 -1
- package/dist/dashboard/assets/card-CtkX66Wd.js.gz +0 -0
- package/dist/dashboard/assets/chevron-left-Bf6CaTSJ.js +1 -0
- package/dist/dashboard/assets/chevron-left-Bf6CaTSJ.js.gz +0 -0
- package/dist/dashboard/assets/cli-tools-CQXVMn4P.js +1 -0
- package/dist/dashboard/assets/cli-tools-CQXVMn4P.js.gz +0 -0
- package/dist/dashboard/assets/{clipboard-button-DJm-9pz7.js → clipboard-button-BQqBQd2n.js} +1 -1
- package/dist/dashboard/assets/clipboard-button-BQqBQd2n.js.gz +0 -0
- package/dist/dashboard/assets/{dialog-8fiE6tec.js → dialog-BEK7KP0H.js} +1 -1
- package/dist/dashboard/assets/dialog-BEK7KP0H.js.gz +0 -0
- package/dist/dashboard/assets/{drawer-CqGYeOrd.js → drawer-BrTEZ8zu.js} +1 -1
- package/dist/dashboard/assets/drawer-BrTEZ8zu.js.gz +0 -0
- package/dist/dashboard/assets/entry-CHWxbS56.js +1 -0
- package/dist/dashboard/assets/entry-CHWxbS56.js.gz +0 -0
- package/dist/dashboard/assets/{entry-BSAsYsmV.js → entry-EBHJ3DrE.js} +1 -1
- package/dist/dashboard/assets/entry-EBHJ3DrE.js.gz +0 -0
- package/dist/dashboard/assets/entry-LCS85hiq.js +4 -0
- package/dist/dashboard/assets/entry-LCS85hiq.js.gz +0 -0
- package/dist/dashboard/assets/{formatters-D-MTJxMr.js → formatters-BAACA727.js} +1 -1
- package/dist/dashboard/assets/formatters-BAACA727.js.gz +0 -0
- package/dist/dashboard/assets/{github-badge-CPK47xQx.js → github-badge-YSCE4UC2.js} +1 -1
- package/dist/dashboard/assets/github-badge-YSCE4UC2.js.gz +0 -0
- package/dist/dashboard/assets/{index-DBNPOfhV.js → index-FQlj0HUT.js} +2 -2
- package/dist/dashboard/assets/index-FQlj0HUT.js.gz +0 -0
- package/dist/dashboard/assets/{inline-BT0zntEr.js → inline-C4gU5iOL.js} +1 -1
- package/dist/dashboard/assets/inline-C4gU5iOL.js.gz +0 -0
- package/dist/dashboard/assets/{input-Da2ZViWg.js → input-BHHX_Tmm.js} +1 -1
- package/dist/dashboard/assets/input-BHHX_Tmm.js.gz +0 -0
- package/dist/dashboard/assets/{layout-BXNeLw6M.js → layout-CajrYUnD.js} +1 -1
- package/dist/dashboard/assets/layout-CajrYUnD.js.gz +0 -0
- package/dist/dashboard/assets/{list-row-BycOrrIV.js → list-row-Bx2I7XUe.js} +1 -1
- package/dist/dashboard/assets/list-row-Bx2I7XUe.js.gz +0 -0
- package/dist/dashboard/assets/live-DNNqRbj2.js +1 -0
- package/dist/dashboard/assets/live-DNNqRbj2.js.gz +0 -0
- package/dist/dashboard/assets/network-C5UBACrJ.js +1 -0
- package/dist/dashboard/assets/network-C5UBACrJ.js.gz +0 -0
- package/dist/dashboard/assets/numbers-CUWAZK4J.js +2 -0
- package/dist/dashboard/assets/numbers-CUWAZK4J.js.gz +0 -0
- package/dist/dashboard/assets/pagination-Xsi9Qx04.js +1 -0
- package/dist/dashboard/assets/pagination-Xsi9Qx04.js.gz +0 -0
- package/dist/dashboard/assets/quota-DvQbD_sr.js +1 -0
- package/dist/dashboard/assets/quota-DvQbD_sr.js.gz +0 -0
- package/dist/dashboard/assets/routing-in0f_IfK.js +1 -0
- package/dist/dashboard/assets/routing-in0f_IfK.js.gz +0 -0
- package/dist/dashboard/assets/{select-BIYIBkMU.js → select-BLULNqTp.js} +1 -1
- package/dist/dashboard/assets/select-BLULNqTp.js.gz +0 -0
- package/dist/dashboard/assets/settings-CcGczj6I.js +1 -0
- package/dist/dashboard/assets/settings-CcGczj6I.js.gz +0 -0
- package/dist/dashboard/assets/shield-BMQNxORh.js +1 -0
- package/dist/dashboard/assets/shield-BMQNxORh.js.gz +0 -0
- package/dist/dashboard/assets/{stack-DfgnFDWY.js → stack-D2lIR0tg.js} +1 -1
- package/dist/dashboard/assets/stack-D2lIR0tg.js.gz +0 -0
- package/dist/dashboard/assets/{state-CkfdYWel.js → state-DTARjfiW.js} +1 -1
- package/dist/dashboard/assets/state-DTARjfiW.js.gz +0 -0
- package/dist/dashboard/assets/{switch-SYm237HG.js → switch-c0oPSnqW.js} +1 -1
- package/dist/dashboard/assets/switch-c0oPSnqW.js.gz +0 -0
- package/dist/dashboard/assets/{useMutation-BQqCRNIi.js → useMutation-m8WGNPrz.js} +1 -1
- package/dist/dashboard/assets/useMutation-m8WGNPrz.js.gz +0 -0
- package/dist/dashboard/console-backgrounds.jpg +0 -0
- package/dist/dashboard/console-backgrounds_initial.jpg +0 -0
- package/dist/dashboard/index.html +2 -2
- package/dist/dashboard/index.html.gz +0 -0
- package/dist/dashboard/precompressed-assets.json +56 -55
- package/migrations/full-postgres/0036_system_prompt_presets_refs.sql +10 -0
- package/migrations/full-postgres/0037_share_concurrency_scope.sql +3 -0
- package/migrations/full-postgres/0038_add_accounts_quota_snapshot.sql +7 -0
- package/migrations/full-postgres/0039_add_telemetry_provider_model_indexes.sql +13 -0
- package/migrations/full-postgres/0040_add_models_dialect.sql +3 -0
- package/migrations/full-postgres/0041_add_daily_checkin_ledger_account_fk.sql +7 -0
- package/migrations/lite-sqlite3/0036_system_prompt_presets_refs.sql +14 -0
- package/migrations/lite-sqlite3/0037_share_concurrency_scope.sql +3 -0
- package/migrations/lite-sqlite3/0038_add_accounts_quota_snapshot.sql +4 -0
- package/migrations/lite-sqlite3/0039_add_telemetry_provider_model_indexes.sql +6 -0
- package/migrations/lite-sqlite3/0040_add_models_dialect.sql +3 -0
- package/migrations/lite-sqlite3/0041_add_daily_checkin_ledger_account_fk.sql +26 -0
- package/package.json +4 -6
- package/scripts/cli/update.ts +15 -41
- package/src/boot/app.ts +58 -2
- package/src/boot/dependencies.ts +786 -732
- package/src/boot/lifecycle.ts +11 -12
- package/src/config/cooldowns.ts +34 -55
- package/src/config/network.ts +1 -1
- package/src/config/package-paths.ts +1 -1
- package/src/config/providers.ts +13 -14
- package/src/config/resources.ts +0 -5
- package/src/config/runtime.ts +2 -5
- package/src/config/spec.ts +26 -6
- package/src/console/api-keys/ban-constants.ts +38 -0
- package/src/console/api-keys/contracts.ts +71 -62
- package/src/console/api-keys/routes.ts +48 -12
- package/src/console/auth/access.ts +4 -4
- package/src/console/auth/account-reset.ts +1 -1
- package/src/console/auth/audit-recorder.ts +1 -1
- package/src/console/auth/lockout.ts +7 -3
- package/src/console/auth/row-coercion.ts +57 -12
- package/src/console/auth/session-resolver.ts +1 -1
- package/src/console/auth/session-service.ts +8 -2
- package/src/console/auth/session.ts +37 -31
- package/src/console/auth/setup-service.ts +4 -3
- package/src/console/auth/user-store.ts +1 -1
- package/src/console/backup/9router-map.ts +0 -3
- package/src/console/backup/contracts.ts +9 -9
- package/src/console/backup/export-endpoint.ts +5 -6
- package/src/console/backup/nine-router.ts +1 -7
- package/src/console/backup/routes.ts +29 -33
- package/src/console/backup/service.ts +468 -468
- package/src/console/backup/store.ts +47 -29
- package/src/console/backup/validate.ts +11 -1
- package/src/console/cli-tools/contracts.ts +7 -7
- package/src/console/cli-tools/injectors/cline.ts +110 -110
- package/src/console/cli-tools/injectors/codex.ts +181 -181
- package/src/console/cli-tools/injectors/copilot.ts +108 -108
- package/src/console/cli-tools/injectors/driver.ts +247 -247
- package/src/console/cli-tools/injectors/jcode.ts +115 -115
- package/src/console/cli-tools/injectors/openclaw.ts +161 -161
- package/src/console/cli-tools/injectors/opencode.ts +153 -153
- package/src/console/cli-tools/routes.ts +1 -1
- package/src/console/cli-tools/service.ts +1 -1
- package/src/console/cli-tools/store.ts +2 -2
- package/src/console/console-router.ts +32 -0
- package/src/console/dashboard-assets.ts +9 -9
- package/src/console/domain-registration.ts +16 -2
- package/src/console/inferhub/client.ts +0 -7
- package/src/console/inferhub/key-store.ts +1 -1
- package/src/console/inferhub/normalize.ts +2 -2
- package/src/console/inferhub/routes.ts +1 -1
- package/src/console/inferhub/types.ts +1 -1
- package/src/console/observability/contracts.ts +1 -1
- package/src/console/observability/live.ts +31 -18
- package/src/console/observability/logs.ts +1 -1
- package/src/console/observability/sse.ts +62 -8
- package/src/console/observability/store.ts +20 -4
- package/src/console/providers/catalog/catalog-projections.ts +1 -1
- package/src/console/providers/catalog/contracts.ts +39 -0
- package/src/console/providers/catalog/model-operations.ts +1 -1
- package/src/console/providers/catalog/provider-operations.ts +95 -17
- package/src/console/providers/catalog/public-model-store.ts +0 -0
- package/src/console/providers/catalog/routes.ts +27 -3
- package/src/console/providers/catalog/store.ts +147 -8
- package/src/console/providers/detail/routes.ts +1 -1
- package/src/console/providers/oauth/callback-listener.ts +100 -18
- package/src/console/providers/oauth/routes.ts +92 -13
- package/src/console/quota/account-quota-global.ts +41 -3
- package/src/console/quota/account-quota-shared.ts +10 -3
- package/src/console/quota/account-quota-tenant.ts +259 -66
- package/src/console/quota/daily-growth-job.ts +308 -0
- package/src/console/quota/native-identity.ts +2 -2
- package/src/console/quota/quota-view.ts +1 -1
- package/src/console/remote-support/routes.ts +1 -1
- package/src/console/remote-support/service.ts +2 -2
- package/src/console/routing/model/contracts.ts +1 -1
- package/src/console/routing/pools/contracts.ts +2 -2
- package/src/console/routing/pools/import-parse.ts +3 -3
- package/src/console/routing/pools/relay-deploy.ts +3 -3
- package/src/console/settings/contracts.ts +29 -2
- package/src/console/settings/store.ts +20 -0
- package/src/console/share/share-router.ts +100 -32
- package/src/console/share/share-stats.ts +4 -4
- package/src/console/share/share-usage.ts +1 -1
- package/src/console/shared/errors.ts +2 -2
- package/src/console/shared/object.ts +18 -0
- package/src/console/shared/read-through-cache.ts +4 -4
- package/src/console/shared/transaction.ts +14 -0
- package/src/console/system-prompt/contracts.ts +89 -12
- package/src/console/system-prompt/routes.ts +98 -45
- package/src/console/updates/apply-update.ts +244 -0
- package/src/console/updates/routes.ts +252 -97
- package/src/main.ts +11 -5
- package/src/network/http2-bidi.ts +1 -1
- package/src/network/ip-utils.ts +298 -0
- package/src/network/outbound-fetch.ts +1 -1
- package/src/network/pool/agent.ts +7 -6
- package/src/network/pool/byte-accounting.ts +1 -1
- package/src/network/pool/resolver.ts +16 -12
- package/src/network/pool/selector.ts +150 -136
- package/src/network/pool-health-machine.ts +1 -1
- package/src/network/rate-limit-verdict.ts +1 -1
- package/src/network/retry.ts +1 -1
- package/src/network/ssrf.ts +20 -71
- package/src/observability/event-log.ts +2 -2
- package/src/observability/event-query.ts +59 -291
- package/src/observability/log-sanitize.ts +1 -1
- package/src/observability/metrics.ts +4 -4
- package/src/observability/payload-capture.ts +6 -5
- package/src/observability/payload-store.ts +1 -1
- package/src/observability/runtime-metrics.ts +1 -1
- package/src/observability/telemetry-buffer.ts +12 -3
- package/src/observability/telemetry-reconciliation.ts +3 -3
- package/src/observability/token-speed.ts +1 -1
- package/src/persistence/connection-url.ts +1 -1
- package/src/persistence/db-handle.ts +1 -1
- package/src/persistence/db-pg.ts +2 -2
- package/src/persistence/db-sqlite.ts +3 -3
- package/src/persistence/dialect.ts +5 -1
- package/src/persistence/readiness.ts +1 -1
- package/src/persistence/redis.ts +1 -1
- package/src/persistence/schema.ts +113 -40
- package/src/persistence/shared-cache.ts +2 -2
- package/src/persistence/sort-order.ts +1 -1
- package/src/persistence/stores/api-key-store.ts +10 -1
- package/src/persistence/stores/share-store.ts +20 -8
- package/src/persistence/system-prompt-store.ts +63 -18
- package/src/persistence/tenant-preferences.ts +1 -1
- package/src/persistence/ttl-cache.ts +3 -3
- package/src/persistence/usage-flush.ts +7 -2
- package/src/protocol/canonical-model.ts +1 -1
- package/src/protocol/codecs.ts +6 -10
- package/src/protocol/dsml.ts +3 -49
- package/src/protocol/image-clamp.ts +2 -2
- package/src/protocol/reasoning-text.ts +1 -1
- package/src/protocol/reasoning.ts +7 -52
- package/src/protocol/registry.ts +50 -15
- package/src/protocol/request/chat.ts +46 -19
- package/src/protocol/request/codex.ts +1 -1
- package/src/protocol/request/messages.ts +46 -14
- package/src/protocol/request/responses.ts +19 -10
- package/src/protocol/response/chat.ts +25 -10
- package/src/protocol/response/codex.ts +1 -1
- package/src/protocol/response/messages.ts +1 -1
- package/src/protocol/response/omp-tool-call-markup.ts +1 -1
- package/src/protocol/response/responses.ts +1 -1
- package/src/protocol/sse-decoder.ts +50 -1
- package/src/protocol/stop-reason.ts +2 -2
- package/src/protocol/think-split.ts +347 -0
- package/src/protocol/translation/attachment-integrity.ts +3 -3
- package/src/protocol/translation/cache-controls.ts +8 -20
- package/src/protocol/translation/capabilities.ts +6 -6
- package/src/protocol/translation/dialect/deepseek.ts +10 -0
- package/src/protocol/translation/dialect/inband.ts +39 -0
- package/src/protocol/translation/dialect/model-thinking.ts +89 -0
- package/src/protocol/translation/dialect/qwen.ts +114 -0
- package/src/protocol/translation/dialect/resolver.ts +83 -0
- package/src/protocol/translation/dialect/rules.ts +93 -0
- package/src/protocol/translation/dialect/thinking-ladder.ts +245 -0
- package/src/protocol/translation/dialect/types.ts +58 -0
- package/src/protocol/translation/quirks.ts +18 -4
- package/src/protocol/translation/thinking.ts +12 -252
- package/src/protocol/translation/tool-id.ts +3 -3
- package/src/protocol/translation/tool-repair.ts +1 -1
- package/src/protocol/upstream/post-json.ts +6 -1
- package/src/providers/adapters/compatible-adapter.ts +42 -3
- package/src/providers/authentication/jwt-validator.ts +0 -5
- package/src/providers/authentication/oauth-client.ts +254 -253
- package/src/providers/authentication/oauth-flow-store.ts +42 -7
- package/src/providers/bundled-discovery-providers.ts +6 -30
- package/src/providers/configured-openai-providers.ts +82 -97
- package/src/providers/default-registry.ts +23 -35
- package/src/providers/discovery/catalog/catalog-format.ts +13 -2
- package/src/providers/discovery/discovery-types.ts +0 -1
- package/src/providers/discovery/openai-model-discovery.ts +2 -2
- package/src/providers/discovery/probe-phases.ts +48 -13
- package/src/providers/discovery/probing-service.ts +6 -8
- package/src/providers/integrations/agentrouter/agentrouter.ts +12 -2
- package/src/providers/integrations/antigravity/antigravity-oauth.ts +0 -1
- package/src/providers/integrations/antigravity/antigravity-protocol.ts +0 -14
- package/src/providers/integrations/antigravity/antigravity.ts +2 -2
- package/src/providers/integrations/buddy/buddy-catalog-shared.ts +1 -1
- package/src/providers/integrations/buddy/buddy-chat-shared.ts +20 -4
- package/src/providers/integrations/buddy/buddy-cli-headers.ts +19 -52
- package/src/providers/integrations/buddy/buddy-identity.ts +86 -0
- package/src/providers/integrations/buddy/buddy-oauth-shared.ts +1 -1
- package/src/providers/integrations/buddy/codebuddy/cn-provider.ts +1 -2
- package/src/providers/integrations/buddy/codebuddy/provider.ts +4 -15
- package/src/providers/integrations/buddy/codebuddy/shared.ts +30 -9
- package/src/providers/integrations/buddy/workbuddy/provider.ts +5 -9
- package/src/providers/integrations/buddy/workbuddy/shared.ts +21 -3
- package/src/providers/integrations/claude/claude-cch.ts +148 -148
- package/src/providers/integrations/claude/fallback-credit.ts +3 -7
- package/src/providers/integrations/cline/cline.ts +1 -1
- package/src/providers/integrations/codex/codex-device-code.ts +101 -101
- package/src/providers/integrations/codex/codex-oauth.ts +0 -1
- package/src/providers/integrations/commandcode/commandcode.ts +75 -115
- package/src/providers/integrations/commandcode/quota.ts +131 -51
- package/src/providers/integrations/cursor/cursor-http2.ts +0 -3
- package/src/providers/integrations/cursor/cursor-quota.ts +2 -6
- package/src/providers/integrations/cursor/cursor-request.ts +0 -87
- package/src/providers/integrations/cursor/cursor-shared.ts +0 -4
- package/src/providers/integrations/cursor/cursor.ts +38 -5
- package/src/providers/integrations/cursor/protocol/framing.ts +0 -56
- package/src/providers/integrations/cursor/protocol/messages.ts +0 -96
- package/src/providers/integrations/factory/factory.ts +9 -9
- package/src/providers/integrations/github/github-discovery.ts +55 -10
- package/src/providers/integrations/github/github-oauth.ts +6 -5
- package/src/providers/integrations/github/github.ts +41 -14
- package/src/providers/integrations/grok/grok-oauth.ts +261 -261
- package/src/providers/integrations/grok/grok-quota-frame.ts +1 -1
- package/src/providers/integrations/grok/grok-quota.ts +16 -7
- package/src/providers/integrations/grok/grok-ratelimit-store.ts +0 -5
- package/src/providers/integrations/grok/grok-ratelimit.ts +3 -0
- package/src/providers/integrations/grok/grok.ts +86 -36
- package/src/providers/integrations/kilo/kilo-oauth.ts +1 -1
- package/src/providers/integrations/kiro/kiro-cache.ts +31 -0
- package/src/providers/integrations/kiro/kiro-catalog.ts +31 -5
- package/src/providers/integrations/kiro/kiro-discovery.ts +47 -43
- package/src/providers/integrations/kiro/kiro-machine-id.ts +6 -3
- package/src/providers/integrations/kiro/kiro-oauth.ts +523 -255
- package/src/providers/integrations/kiro/kiro-quota.ts +35 -47
- package/src/providers/integrations/kiro/kiro-request.ts +8 -11
- package/src/providers/integrations/kiro/kiro-stream.ts +13 -0
- package/src/providers/integrations/kiro/kiro.ts +81 -68
- package/src/providers/integrations/muse/muse-oauth.ts +0 -1
- package/src/providers/integrations/muse/muse-quota.ts +0 -4
- package/src/providers/integrations/opencode/fingerprint.ts +7 -5
- package/src/providers/integrations/opencode/provider.ts +17 -24
- package/src/providers/integrations/qoder/qoder-catalog.ts +85 -8
- package/src/providers/integrations/qoder/qoder-oauth.ts +165 -25
- package/src/providers/integrations/qoder/qoder-quota.ts +48 -6
- package/src/providers/integrations/qoder/qoder-realm.ts +10 -31
- package/src/providers/integrations/qoder/qoder-sse.ts +159 -0
- package/src/providers/integrations/qoder/qoder-umid.ts +21 -25
- package/src/providers/integrations/qoder/qoder-virtual.ts +12 -0
- package/src/providers/integrations/qoder/qoder.ts +354 -62
- package/src/providers/integrations/shared/connect.ts +0 -54
- package/src/providers/integrations/shared/hash.ts +15 -0
- package/src/providers/integrations/shared/session-cache.ts +136 -0
- package/src/providers/integrations/trae/trae-cache.ts +7 -87
- package/src/providers/integrations/trae/trae-catalog.ts +1 -6
- package/src/providers/integrations/trae/trae-protocol.ts +17 -8
- package/src/providers/integrations/trae/trae-realm.ts +0 -7
- package/src/providers/integrations/trae/trae.ts +53 -10
- package/src/providers/integrations/xai/xai-oauth.ts +5 -5
- package/src/providers/integrations/zcode-start-plan/gifts.ts +265 -0
- package/src/providers/integrations/zcode-start-plan/oauth.ts +154 -0
- package/src/providers/integrations/zcode-start-plan/provider.ts +131 -0
- package/src/providers/integrations/zcode-start-plan/quota.ts +152 -0
- package/src/providers/integrations/zcode-start-plan/zcode_prompt.json +10 -0
- package/src/providers/operations/account-error-classification.ts +131 -116
- package/src/providers/operations/account-health-service.ts +31 -39
- package/src/providers/operations/account-reset-service.ts +58 -1
- package/src/providers/operations/cli-platform.ts +31 -5
- package/src/providers/operations/client-version-resolver.ts +4 -6
- package/src/providers/operations/client-versions.ts +10 -7
- package/src/providers/operations/provider-catalog-seeder.ts +2 -0
- package/src/providers/operations/provider-catalog-service.ts +18 -2
- package/src/providers/operations/provider-credential-service.ts +19 -2
- package/src/providers/provider-contracts.ts +18 -7
- package/src/providers/provider-metadata.ts +17 -2
- package/src/providers/provider-model-definition.ts +22 -1
- package/src/providers/provider-probe.ts +4 -2
- package/src/providers/provider-usage.ts +81 -16
- package/src/providers/quota/cache.ts +29 -0
- package/src/providers/quota/quota-contracts.ts +4 -0
- package/src/providers/quota/refresh.ts +27 -4
- package/src/providers/quota/snapshot.ts +94 -0
- package/src/providers/search/search-provider.ts +1 -3
- package/src/providers/search/search-providers.ts +2 -1
- package/src/security/abuse.ts +4 -4
- package/src/security/admission/service.ts +2 -2
- package/src/security/admission/ttl.ts +61 -61
- package/src/security/api-key-auth.ts +16 -4
- package/src/security/client-router-fingerprint.ts +4 -4
- package/src/security/credential-codec.ts +3 -3
- package/src/security/csrf.ts +2 -2
- package/src/security/ip-boundary.ts +16 -189
- package/src/security/model-access-rule.ts +1 -1
- package/src/security/outbound-headers.ts +18 -0
- package/src/shared/invoke-guard.ts +47 -0
- package/src/transport/contracts.ts +13 -4
- package/src/transport/dispatch/attempt-finalize.ts +1 -1
- package/src/transport/dispatch/capture-policy.ts +34 -37
- package/src/transport/dispatch/empty-stream.ts +23 -20
- package/src/transport/dispatch/generation.ts +25 -45
- package/src/transport/dispatch/native-shared.ts +127 -0
- package/src/transport/dispatch/proxy-request.ts +3 -1
- package/src/transport/dispatch/responses-compact.ts +25 -46
- package/src/transport/dispatch/streaming/encoder.ts +18 -72
- package/src/transport/dispatch/streaming/finalize.ts +2 -2
- package/src/transport/dispatch/streaming/stream.ts +7 -3
- package/src/transport/dispatch/streaming/thinking-loop.ts +1 -1
- package/src/transport/dispatch/systemone.ts +20 -48
- package/src/transport/dispatch/token-count.ts +41 -38
- package/src/transport/dispatch/websearch.ts +19 -45
- package/src/transport/drain-endpoint.ts +1 -1
- package/src/transport/failure-policy.ts +6 -0
- package/src/transport/guards/auth-guard.ts +0 -6
- package/src/transport/guards/body-policy.ts +3 -3
- package/src/transport/guards/headroom-guard.ts +1 -1
- package/src/transport/guards/pipeline.ts +1 -1
- package/src/transport/guards/request-context.ts +12 -17
- package/src/transport/request/preparer.ts +286 -7
- package/src/transport/request/rtk/compress-request.ts +3 -3
- package/src/transport/request/rtk/rtk-constants.ts +2 -2
- package/src/transport/request/rtk/rtk-filters.ts +8 -8
- package/src/transport/request/state.ts +1 -13
- package/src/transport/request/system-prompt-injection.ts +74 -11
- package/src/transport/request/tenant-preferences.ts +22 -0
- package/src/transport/routing/candidate-index.ts +2 -2
- package/src/transport/routing/round-robin.ts +1 -1
- package/src/transport/routing/route-catalog.ts +17 -18
- package/src/transport/routing/route-model.ts +126 -33
- package/src/transport/routing/router.ts +152 -37
- package/src/transport/shutdown-notice.ts +2 -2
- package/src/transport/surface/adapters.ts +3 -3
- package/src/transport/surface/chat/encode.ts +7 -7
- package/src/transport/surface/chat/parse.ts +7 -7
- package/src/transport/surface/completion.ts +2 -2
- package/src/transport/surface/content-parts.ts +1 -1
- package/src/transport/surface/dialects.ts +2 -2
- package/src/transport/surface/messages/parse.ts +3 -3
- package/src/transport/surface/responses/adapter.ts +1 -1
- package/src/transport/surface/responses/errors.ts +0 -9
- package/src/transport/vision-handoff-cache.ts +58 -0
- package/src/transport/vision-handoff.ts +376 -0
- package/src/workers/account-identity-sweep.ts +2 -2
- package/src/workers/buddy-growth-tasks.ts +392 -346
- package/src/workers/daily-checkin.ts +102 -49
- package/src/workers/maintenance-lease.ts +2 -35
- package/src/workers/oauth-refresh-sweep.ts +8 -1
- package/src/workers/quota-refresh-sweep.ts +120 -82
- package/src/workers/scheduler.ts +67 -4
- package/src/workers/sweep-runner.ts +24 -5
- package/src/workers/zcode-gift-claim.ts +108 -0
- package/dist/dashboard/assets/AccountCooldown-Bjo92Qat.js +0 -1
- package/dist/dashboard/assets/AccountCooldown-Bjo92Qat.js.gz +0 -0
- package/dist/dashboard/assets/BannedPage-ChG5svFb.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-BIcWQun7.js.gz +0 -0
- package/dist/dashboard/assets/CliToolsPage-Bf2VelDe.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-DCgm5g6g.js +0 -1
- package/dist/dashboard/assets/CombosPage-DCgm5g6g.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-a8mD0QF1.js +0 -1
- package/dist/dashboard/assets/ConsoleLogPage-a8mD0QF1.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-ggeMSjte.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-DzSWzE_j.js +0 -1
- package/dist/dashboard/assets/HealthEventsModal-DzSWzE_j.js.gz +0 -0
- package/dist/dashboard/assets/LoginPage-U6AKaJcF.js.gz +0 -0
- package/dist/dashboard/assets/ModelPicker-BMS2t8BA.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-D2D4IiHl.js +0 -1
- package/dist/dashboard/assets/OverviewPage-D2D4IiHl.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-CrNf7j_X.js +0 -5
- package/dist/dashboard/assets/ProviderDetailPage-CrNf7j_X.js.gz +0 -0
- package/dist/dashboard/assets/ProviderIcon-FB6yS4v1.js +0 -1
- package/dist/dashboard/assets/ProviderIcon-FB6yS4v1.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-BSDbquc1.js +0 -1
- package/dist/dashboard/assets/ProvidersPage-BSDbquc1.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-lsrx7NYg.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-L0skzML8.js +0 -1
- package/dist/dashboard/assets/QuotaPage-L0skzML8.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-CmAbjRHW.js +0 -1
- package/dist/dashboard/assets/SettingsPage-CmAbjRHW.js.gz +0 -0
- package/dist/dashboard/assets/SetupPage-f0DE1h0-.js.gz +0 -0
- package/dist/dashboard/assets/SortableList-ClzpwyET.js +0 -1
- package/dist/dashboard/assets/SortableList-ClzpwyET.js.gz +0 -0
- package/dist/dashboard/assets/StudioPage-Cq5tW7hW.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-Dfc9rWQn.js +0 -56
- package/dist/dashboard/assets/UsagePage-Dfc9rWQn.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-C38iXoO4.js +0 -1
- package/dist/dashboard/assets/api-keys-C38iXoO4.js.gz +0 -0
- package/dist/dashboard/assets/app-a159k0ub.css +0 -2
- package/dist/dashboard/assets/app-a159k0ub.css.gz +0 -0
- package/dist/dashboard/assets/assert-DbRA8EG2.js +0 -15
- package/dist/dashboard/assets/assert-DbRA8EG2.js.gz +0 -0
- package/dist/dashboard/assets/badge-D9CS6dZk.js.gz +0 -0
- package/dist/dashboard/assets/banner-DympBNq4.js.gz +0 -0
- package/dist/dashboard/assets/card-ibRWU_uT.js.gz +0 -0
- package/dist/dashboard/assets/cli-tools-63qBGaRw.js +0 -1
- package/dist/dashboard/assets/cli-tools-63qBGaRw.js.gz +0 -0
- package/dist/dashboard/assets/clipboard-button-DJm-9pz7.js.gz +0 -0
- package/dist/dashboard/assets/dialog-8fiE6tec.js.gz +0 -0
- package/dist/dashboard/assets/drawer-CqGYeOrd.js.gz +0 -0
- package/dist/dashboard/assets/entry-BSAsYsmV.js.gz +0 -0
- package/dist/dashboard/assets/entry-CiIQULZD.js +0 -4
- package/dist/dashboard/assets/entry-CiIQULZD.js.gz +0 -0
- package/dist/dashboard/assets/entry-Daz0lJ-W.js +0 -1
- package/dist/dashboard/assets/entry-Daz0lJ-W.js.gz +0 -0
- package/dist/dashboard/assets/formatters-D-MTJxMr.js.gz +0 -0
- package/dist/dashboard/assets/github-badge-CPK47xQx.js.gz +0 -0
- package/dist/dashboard/assets/index-DBNPOfhV.js.gz +0 -0
- package/dist/dashboard/assets/inline-BT0zntEr.js.gz +0 -0
- package/dist/dashboard/assets/input-Da2ZViWg.js.gz +0 -0
- package/dist/dashboard/assets/layout-BXNeLw6M.js.gz +0 -0
- package/dist/dashboard/assets/list-row-BycOrrIV.js.gz +0 -0
- package/dist/dashboard/assets/live-DCQNZpF7.js +0 -1
- package/dist/dashboard/assets/live-DCQNZpF7.js.gz +0 -0
- package/dist/dashboard/assets/network-9Yj2ZyLo.js +0 -1
- package/dist/dashboard/assets/network-9Yj2ZyLo.js.gz +0 -0
- package/dist/dashboard/assets/numbers-CEagj6ZZ.js +0 -2
- package/dist/dashboard/assets/numbers-CEagj6ZZ.js.gz +0 -0
- package/dist/dashboard/assets/quota-Caw7wqci.js +0 -1
- package/dist/dashboard/assets/quota-Caw7wqci.js.gz +0 -0
- package/dist/dashboard/assets/routing-CO9zCUus.js +0 -1
- package/dist/dashboard/assets/routing-CO9zCUus.js.gz +0 -0
- package/dist/dashboard/assets/select-BIYIBkMU.js.gz +0 -0
- package/dist/dashboard/assets/settings-jS3-PNSA.js +0 -1
- package/dist/dashboard/assets/settings-jS3-PNSA.js.gz +0 -0
- package/dist/dashboard/assets/stack-DfgnFDWY.js.gz +0 -0
- package/dist/dashboard/assets/state-CkfdYWel.js.gz +0 -0
- package/dist/dashboard/assets/stream-retry-5QnVuSSo.js +0 -1
- package/dist/dashboard/assets/stream-retry-5QnVuSSo.js.gz +0 -0
- package/dist/dashboard/assets/switch-SYm237HG.js.gz +0 -0
- package/dist/dashboard/assets/useMutation-BQqCRNIi.js.gz +0 -0
- package/src/network/response-headers.ts +0 -39
- package/src/protocol/response/dsml.ts +0 -38
- package/src/providers/integrations/xiaomi-mimo/think-stream.ts +0 -229
- /package/dist/dashboard/assets/{app-BayTTy01.js → app-Bgd3zANQ.js} +0 -0
- /package/dist/dashboard/assets/{app-BayTTy01.js.gz → app-Bgd3zANQ.js.gz} +0 -0
- /package/dist/dashboard/assets/brand/{og_bansos.webp → og_bansos_initial.webp} +0 -0
- /package/dist/dashboard/assets/brand/{og_image.webp → og_image_initial.webp} +0 -0
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { asCoercedNumber, asRecord, asTrimmedString } from "../../../shared/coerce";
|
|
2
|
+
import type { FetchLike, ProviderQuotaResult, ProviderQuotaWindow } from "../../quota/quota-contracts";
|
|
3
|
+
|
|
4
|
+
const BALANCE_URL = "https://zcode.z.ai/api/v1/zcode-plan/billing/balance?app_version=3.16.3";
|
|
5
|
+
const DEVICE_MID = "9f1b0d4e-3c2a-4e7b-9f1d-6a2c8b5e0d31";
|
|
6
|
+
const APP_VERSION = "3.16.3";
|
|
7
|
+
|
|
8
|
+
function instant(value: unknown): string | null {
|
|
9
|
+
const seconds = asCoercedNumber(value);
|
|
10
|
+
if (seconds === undefined || seconds <= 0) return null;
|
|
11
|
+
const date = new Date(seconds * 1000);
|
|
12
|
+
return Number.isFinite(date.getTime()) ? date.toISOString() : null;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function boundary(values: readonly unknown[], latest: boolean): string | null {
|
|
16
|
+
const instants = values.map(instant).filter((value): value is string => value !== null);
|
|
17
|
+
if (instants.length === 0) return null;
|
|
18
|
+
return instants.reduce((a, b) => (latest ? a > b : a < b) ? a : b);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// The upstream capabilities, not the display name, declare which models a grant serves.
|
|
22
|
+
function modelIds(value: unknown): readonly string[] {
|
|
23
|
+
if (!Array.isArray(value)) return [];
|
|
24
|
+
return [...new Set(value.flatMap((entry: unknown) => {
|
|
25
|
+
const capability = asTrimmedString(entry);
|
|
26
|
+
if (capability === undefined || (capability.includes(":") && !capability.startsWith("model:"))) return [];
|
|
27
|
+
const model = capability.replace(/^model:/, "").trim().toLowerCase();
|
|
28
|
+
return model ? [model] : [];
|
|
29
|
+
}))];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function periodLabel(period: string | undefined): string {
|
|
33
|
+
if (period === "daily_night") return "nightly";
|
|
34
|
+
if (period === "one_time") return "one-time";
|
|
35
|
+
return period ?? "grant";
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function grantLabel(name: string): string {
|
|
39
|
+
return /^(?:zcode\s+)?start plan$/i.test(name) ? "Start" : name;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function percent(value: number | null, limit: number | null): number | null {
|
|
43
|
+
return value !== null && limit !== null && limit > 0
|
|
44
|
+
? Math.min(100, Math.max(0, value / limit * 100))
|
|
45
|
+
: null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function samePlan(bucket: Record<string, unknown>, plan: Record<string, unknown>): boolean {
|
|
49
|
+
const userPlan = asTrimmedString(bucket["user_plan_id"]);
|
|
50
|
+
const owner = asTrimmedString(plan["user_plan_id"]);
|
|
51
|
+
if (userPlan !== undefined && owner !== undefined) return userPlan === owner;
|
|
52
|
+
const id = asTrimmedString(bucket["plan_id"]);
|
|
53
|
+
return id !== undefined && id === asTrimmedString(plan["plan_id"]);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Billing shape verified against opencode-zcode-auth 0.1.42; see the provider guide. */
|
|
57
|
+
export async function fetchZcodeStartPlanQuota(
|
|
58
|
+
credential: string,
|
|
59
|
+
fetcher: FetchLike,
|
|
60
|
+
): Promise<ProviderQuotaResult> {
|
|
61
|
+
const source = "zcode-start";
|
|
62
|
+
try {
|
|
63
|
+
const response = await fetcher(BALANCE_URL, {
|
|
64
|
+
headers: {
|
|
65
|
+
authorization: `Bearer ${credential}`,
|
|
66
|
+
accept: "application/json",
|
|
67
|
+
"x-device-mid": DEVICE_MID,
|
|
68
|
+
"x-zcode-app-version": APP_VERSION,
|
|
69
|
+
"x-client-language": "en-US",
|
|
70
|
+
},
|
|
71
|
+
signal: AbortSignal.timeout(15_000),
|
|
72
|
+
});
|
|
73
|
+
if (!response.ok) return { source, plan: null, windows: [], error: `plan billing answered ${response.status}` };
|
|
74
|
+
const payload = asRecord(await response.json());
|
|
75
|
+
const code = payload?.["code"];
|
|
76
|
+
if (!payload || payload["success"] === false ||
|
|
77
|
+
(code !== undefined && ![0, 200, "0", "200"].includes(code as string | number))) {
|
|
78
|
+
return { source, plan: null, windows: [], error: "plan billing rejected request" };
|
|
79
|
+
}
|
|
80
|
+
const data = asRecord(payload["data"]);
|
|
81
|
+
if (!data || !Array.isArray(data["balances"]) || !Array.isArray(data["plans"])) {
|
|
82
|
+
return { source, plan: null, windows: [], error: "plan billing returned an unreadable balance" };
|
|
83
|
+
}
|
|
84
|
+
const plans = data["plans"].flatMap((value: unknown) => {
|
|
85
|
+
const plan = asRecord(value);
|
|
86
|
+
return plan ? [plan] : [];
|
|
87
|
+
});
|
|
88
|
+
const windows: ProviderQuotaWindow[] = [];
|
|
89
|
+
const names = new Set<string>();
|
|
90
|
+
const represented = new Set<Record<string, unknown>>();
|
|
91
|
+
for (const raw of data["balances"]) {
|
|
92
|
+
const bucket = asRecord(raw);
|
|
93
|
+
if (!bucket) continue;
|
|
94
|
+
const owner = plans.find((plan) => samePlan(bucket, plan));
|
|
95
|
+
if (owner) represented.add(owner);
|
|
96
|
+
const name = asTrimmedString(owner?.["name"]) ?? "Start Plan";
|
|
97
|
+
names.add(name);
|
|
98
|
+
const entitlement = (Array.isArray(owner?.["entitlements"]) ? owner["entitlements"] : [])
|
|
99
|
+
.map(asRecord).find((entry) => entry?.["entitlement_id"] === bucket["entitlement_id"]);
|
|
100
|
+
const period = asTrimmedString(entitlement?.["period"]);
|
|
101
|
+
const limit = asCoercedNumber(bucket["total_units"]) ?? null;
|
|
102
|
+
const reportedUsed = asCoercedNumber(bucket["used_units"]);
|
|
103
|
+
const reportedRemaining = asCoercedNumber(bucket["remaining_units"]);
|
|
104
|
+
const used = reportedUsed ?? (limit !== null && reportedRemaining !== undefined ? Math.max(0, limit - reportedRemaining) : null);
|
|
105
|
+
const remaining = reportedRemaining ?? (limit !== null && used !== null ? Math.max(0, limit - used) : null);
|
|
106
|
+
const models = modelIds(bucket["capabilities"] ?? entitlement?.["capabilities"]);
|
|
107
|
+
const expired = asTrimmedString(owner?.["status"])?.toLowerCase() === "expired";
|
|
108
|
+
windows.push({
|
|
109
|
+
kind: period ?? "grant",
|
|
110
|
+
label: `${asTrimmedString(bucket["show_name"]) ?? (models.join(", ") || "GLM")} · ${grantLabel(name)} · ${periodLabel(period)}`,
|
|
111
|
+
accounting: "independent",
|
|
112
|
+
modelIds: models,
|
|
113
|
+
startsAt: boundary([owner?.["starts_at"], bucket["period_start"]], true),
|
|
114
|
+
resetsAt: boundary([bucket["expires_at"], bucket["period_end"], owner?.["ends_at"]], false),
|
|
115
|
+
usedPercent: expired ? 100 : percent(used, limit),
|
|
116
|
+
remainingPercent: expired ? 0 : percent(remaining, limit),
|
|
117
|
+
used,
|
|
118
|
+
limit,
|
|
119
|
+
remaining,
|
|
120
|
+
recurring: !expired && (period === "daily" || period === "daily_night"),
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
// The balance may list a future plan before it has materialized any buckets.
|
|
124
|
+
for (const plan of plans) {
|
|
125
|
+
const startsAt = instant(plan["starts_at"]);
|
|
126
|
+
if (represented.has(plan) || !startsAt || Date.parse(startsAt) <= Date.now() || plan["status"] === "expired") continue;
|
|
127
|
+
const name = asTrimmedString(plan["name"]) ?? "Start Plan";
|
|
128
|
+
names.add(name);
|
|
129
|
+
const entitlements = Array.isArray(plan["entitlements"]) ? plan["entitlements"] : [];
|
|
130
|
+
const pending = entitlements.length > 0 ? entitlements : [{}];
|
|
131
|
+
for (const raw of pending) {
|
|
132
|
+
const entitlement = asRecord(raw);
|
|
133
|
+
windows.push({
|
|
134
|
+
kind: "grant", label: `${grantLabel(name)} · ${asTrimmedString(entitlement?.["show_name"]) ?? "not started yet"}`,
|
|
135
|
+
accounting: "independent", modelIds: modelIds(entitlement?.["capabilities"]), startsAt,
|
|
136
|
+
resetsAt: instant(plan["ends_at"]), usedPercent: null, remainingPercent: null,
|
|
137
|
+
remaining: null, used: null, limit: asCoercedNumber(entitlement?.["grant_units"]) ?? null,
|
|
138
|
+
recurring: false,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return { source, plan: [...names].join(", ") || null, windows, error: null };
|
|
143
|
+
} catch (error) {
|
|
144
|
+
const name = error instanceof Error ? error.name : "";
|
|
145
|
+
const message = name === "TimeoutError" || name === "AbortError"
|
|
146
|
+
? "plan billing operation timed out or was aborted"
|
|
147
|
+
: name === "SyntaxError"
|
|
148
|
+
? "plan billing returned unreadable JSON"
|
|
149
|
+
: "plan billing network request failed";
|
|
150
|
+
return { source, plan: null, windows: [], error: message };
|
|
151
|
+
}
|
|
152
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_source": "ZCode 3.14.5 client Messages prompt; preserve text with the pinned identity version",
|
|
3
|
+
"cliPrefix": "You are ZCode, an interactive coding agent",
|
|
4
|
+
"stableSections": [
|
|
5
|
+
"\nYou are an interactive ZCode agent that helps users with software engineering tasks.\n\nIMPORTANT: Assist with authorized security testing, defensive security, CTF challenges, and educational contexts. Refuse requests for destructive techniques, DoS attacks, mass targeting, supply chain compromise, or detection evasion for malicious purposes. Dual-use security tools (C2 frameworks, credential testing, exploit development) require clear authorization context: pentesting engagements, CTF competitions, security research, or defensive use cases.\n\n# Harness\n- Text you output outside of tool use is displayed to the user as Github-flavored markdown in a terminal.\n- Tools run behind a user-selected permission mode; a denied call means the user declined it — adjust, don't retry verbatim.\n- The system may send updates, reminders, or modifications to rules via mid-conversation system turns. These are system-controlled, unlike function results. Hooks may intercept tool calls; treat hook output as user feedback.\n- Prefer the dedicated file/search tools over shell commands when one fits. Independent tool calls can run in parallel in one response.\n- Reference code as `file_path:line_number` — it's clickable.",
|
|
6
|
+
"# ZCode Desktop Context\n\n### Files & URLs\n- Return local web URLs as Markdown links (e.g., [label](http://127.0.0.1:8080)).\n- File should be an absolute path or include the workspace folder segment so it can be resolved relative to the workspace.\n- Unless otherwise specified, return local file references as Markdown links (e.g., [name.md](/absolute/path/to/name.md)).\n\n### Inline Code Comments\n- Use the ::code-comment{...} directive when you need to attach feedback directly to specific code lines.\n- Emit one directive per inline comment; emit none when there are no actionable inline comments.\n- Required attributes: title (short label), body (one-paragraph explanation), file (path to the file).\n- Optional attributes: start, end (1-based line numbers), priority (0-3).\n- file should be an absolute path or include the workspace folder segment so it can be resolved relative to the workspace.\n- Keep line ranges tight; end defaults to start.\n- Example: ::code-comment{title=\"[P2] Off-by-one\" body=\"Loop iterates past the end when length is 0.\" file=\"/path/to/foo.ts\" start=10 end=11 priority=2}",
|
|
7
|
+
"# Working style\n\nWhen you have enough information to act, act. Do not re-derive facts already established in the conversation, re-litigate a decision the user has already made, or narrate options you will not pursue. If you are weighing a choice, give a recommendation, not an exhaustive survey. Prefer reading the actual file or running the actual command over reasoning about what it probably contains. When a signal pattern-matches to a known failure, check that the evidence actually supports that specific diagnosis before acting on it."
|
|
8
|
+
],
|
|
9
|
+
"environmentTemplate": "# Environment\nYou have been invoked in the following environment:\n - Primary working directory: {cwd}\n - Platform: {platform}\n - Shell: {shell}\n - You are powered by the model named {provider}/{model}."
|
|
10
|
+
}
|
|
@@ -57,6 +57,26 @@ const UNCLASSIFIED_COOLDOWN_MS = resolveAccountUnclassifiedCooldownMs;
|
|
|
57
57
|
* which let the account re-enter rotation inside the window.
|
|
58
58
|
*/
|
|
59
59
|
const GROK_QUOTA_COOLDOWN_MS = 24 * 60 * 60 * 1000;
|
|
60
|
+
/**
|
|
61
|
+
* Trae answers `4013 "AI service is not available in the current region now."`
|
|
62
|
+
* when the upstream does not serve the account's region. A day because the
|
|
63
|
+
* verdict is durable: it reproduces on every request until the region opens,
|
|
64
|
+
* and the account must not be re-dialled every thirty seconds for the whole
|
|
65
|
+
* window.
|
|
66
|
+
*/
|
|
67
|
+
const TRAE_REGION_COOLDOWN_MS = 24 * 60 * 60 * 1000;
|
|
68
|
+
/**
|
|
69
|
+
* Trae's free-tier quota (`4008` / "exceeded the quota") resets daily, so its
|
|
70
|
+
* exhaustion is a full-day model backoff — never the generic 1h fallback,
|
|
71
|
+
* which let the account re-enter rotation inside the window.
|
|
72
|
+
*/
|
|
73
|
+
const TRAE_QUOTA_COOLDOWN_MS = 24 * 60 * 60 * 1000;
|
|
74
|
+
/**
|
|
75
|
+
* Qoder's quota verdicts (`110` billing-daily-count, the upstream-499 close)
|
|
76
|
+
* are per-model: one model's spend cap does not drain another. A day matches
|
|
77
|
+
* the daily billing window the provider names.
|
|
78
|
+
*/
|
|
79
|
+
const QODER_QUOTA_COOLDOWN_MS = 24 * 60 * 60 * 1000;
|
|
60
80
|
/**
|
|
61
81
|
* The buddy family's policy/channel block (`11140`, `11128`) is a *base* backoff,
|
|
62
82
|
* not a flat penalty — see {@link resolveAccountPolicyBlockCooldownMs}. It used
|
|
@@ -586,20 +606,10 @@ interface AccountErrorSignals {
|
|
|
586
606
|
readonly staticToken: boolean;
|
|
587
607
|
readonly credentialKind: string | undefined;
|
|
588
608
|
/** Buddy-family provider: the only family with provider-specific codes below. */
|
|
589
|
-
readonly buddy: boolean;
|
|
590
|
-
/**
|
|
591
|
-
* The upstream named a restriction on this account, not a refusal of the
|
|
592
|
-
* request. Only the `11140` shape carries `displayMsg` about the account
|
|
593
|
-
* being restricted, so the policy arm can tell a ban from a bad request
|
|
594
|
-
* instead of treating both as one cooldown.
|
|
595
|
-
*/
|
|
596
609
|
readonly buddyBanned: boolean;
|
|
597
610
|
readonly priceRefusal: boolean;
|
|
598
|
-
readonly deploymentInfraError: boolean;
|
|
599
611
|
readonly quotaSignal: boolean;
|
|
600
612
|
readonly policyRejection: boolean;
|
|
601
|
-
readonly requestScoped: boolean;
|
|
602
|
-
readonly channelRejection: boolean;
|
|
603
613
|
readonly hostedToolFailure: boolean;
|
|
604
614
|
readonly modelEntitlementFailure: boolean;
|
|
605
615
|
readonly queuedUpstream: boolean;
|
|
@@ -793,7 +803,20 @@ export function classifyAccountError(
|
|
|
793
803
|
const modelBlockHits = Math.max(1, Math.floor(options?.modelBlockHits ?? 1));
|
|
794
804
|
const staticToken = options?.staticToken === true;
|
|
795
805
|
const credentialKind = options?.credentialKind;
|
|
796
|
-
|
|
806
|
+
|
|
807
|
+
// Start Plan is a separate provider: its named trial refusal rests only that model.
|
|
808
|
+
const providerId = options?.providerId ?? (error instanceof GatewayError ? error.details.providerId : undefined);
|
|
809
|
+
const refusedModel = statusCode === 400
|
|
810
|
+
? /no active limited quota for ([\w.-]+)/i.exec(message)?.[1]
|
|
811
|
+
: statusCode === 429
|
|
812
|
+
? /limited quota(?: for)? ([\w.-]+)(?: is)? exhausted/i.exec(message)?.[1]
|
|
813
|
+
: undefined;
|
|
814
|
+
if (providerId === "zcode-start" && modelId !== undefined && refusedModel !== undefined &&
|
|
815
|
+
refusedModel.toLowerCase().replace(/-trial$/, "") === modelId.toLowerCase().replace(/-trial$/, "")) {
|
|
816
|
+
const cooldownMs = statedCooldownMs ?? DEFAULT_QUOTA_COOLDOWN_MS();
|
|
817
|
+
return result("model_unreachable", "cooldown", cooldownMs, new Date(Date.now() + cooldownMs),
|
|
818
|
+
reason("Start Plan trial unavailable for this model"), origin === "upstream");
|
|
819
|
+
}
|
|
797
820
|
|
|
798
821
|
// A price refusal is a verdict on the *request*, not on the account. inferhub
|
|
799
822
|
// answers 402 with `no provider's ask matches your max-per-mtok bid`: the
|
|
@@ -869,6 +892,7 @@ export function classifyAccountError(
|
|
|
869
892
|
// keeps serving its other models.
|
|
870
893
|
providerCode === "112" ||
|
|
871
894
|
providerCode === "110" ||
|
|
895
|
+
providerCode === "116" ||
|
|
872
896
|
providerCode === "insufficient_quota" ||
|
|
873
897
|
providerCode === "quota_exceeded" ||
|
|
874
898
|
providerCode === "subscription:free-usage-exhausted" ||
|
|
@@ -1067,7 +1091,8 @@ export function classifyAccountError(
|
|
|
1067
1091
|
// for all other models. Disabling the whole account breaks healthy models.
|
|
1068
1092
|
const modelEntitlementFailure =
|
|
1069
1093
|
statusCode === 403 &&
|
|
1070
|
-
(
|
|
1094
|
+
((providerId === "commandcode" && lower.includes("model_not_in_plan")) ||
|
|
1095
|
+
lower.includes("not have a valid license") ||
|
|
1071
1096
|
lower.includes("valid license of this product") ||
|
|
1072
1097
|
lower.includes("model not supported") ||
|
|
1073
1098
|
lower.includes("not entitled to") ||
|
|
@@ -1131,14 +1156,10 @@ export function classifyAccountError(
|
|
|
1131
1156
|
modelBlockHits,
|
|
1132
1157
|
staticToken,
|
|
1133
1158
|
credentialKind,
|
|
1134
|
-
buddy,
|
|
1135
1159
|
buddyBanned,
|
|
1136
1160
|
priceRefusal,
|
|
1137
|
-
deploymentInfraError,
|
|
1138
1161
|
quotaSignal,
|
|
1139
1162
|
policyRejection,
|
|
1140
|
-
requestScoped,
|
|
1141
|
-
channelRejection,
|
|
1142
1163
|
hostedToolFailure,
|
|
1143
1164
|
modelEntitlementFailure,
|
|
1144
1165
|
queuedUpstream,
|
|
@@ -1225,101 +1246,6 @@ const ACCOUNT_ERROR_RULES: readonly AccountErrorRule[] = [
|
|
|
1225
1246
|
false,
|
|
1226
1247
|
),
|
|
1227
1248
|
},
|
|
1228
|
-
{
|
|
1229
|
-
name: "deployment-infrastructure",
|
|
1230
|
-
match: (s) => s.deploymentInfraError,
|
|
1231
|
-
decide: (s) =>
|
|
1232
|
-
s.result(
|
|
1233
|
-
"server_error",
|
|
1234
|
-
"cooldown",
|
|
1235
|
-
60_000,
|
|
1236
|
-
null,
|
|
1237
|
-
`Proxy deployment error: ${s.message.slice(0, 160)}`,
|
|
1238
|
-
false,
|
|
1239
|
-
),
|
|
1240
|
-
},
|
|
1241
|
-
{
|
|
1242
|
-
name: "model-frequency-window",
|
|
1243
|
-
match: (s) => s.lower.includes("frequency limit") && s.lower.includes("switch to the other models"),
|
|
1244
|
-
decide: (s) => {
|
|
1245
|
-
const cooldownMs = s.statedCooldownMs ?? DEFAULT_QUOTA_COOLDOWN_MS();
|
|
1246
|
-
return s.result(
|
|
1247
|
-
"rate_limit_transient",
|
|
1248
|
-
"cooldown",
|
|
1249
|
-
cooldownMs,
|
|
1250
|
-
new Date(Date.now() + cooldownMs),
|
|
1251
|
-
s.reason("Provider frequency limit for this model"),
|
|
1252
|
-
// Provider-scoped evidence still has to reach the writer: a stream frame
|
|
1253
|
-
// carries no credential detail, so `canMutate` is false and without this
|
|
1254
|
-
// the pair would be retried for the whole window it was closed for.
|
|
1255
|
-
s.origin === "upstream" && s.scope === "provider",
|
|
1256
|
-
);
|
|
1257
|
-
},
|
|
1258
|
-
},
|
|
1259
|
-
{
|
|
1260
|
-
name: "content-policy-block",
|
|
1261
|
-
match: (s) => s.policyRejection && s.buddy,
|
|
1262
|
-
decide: (s) => {
|
|
1263
|
-
// A ban is a verdict on the credential, and cooldown cannot answer it:
|
|
1264
|
-
// the account is refused, not throttled. Disabling is the fix, and
|
|
1265
|
-
// disabling *immediately* is a feature — a client's retry budget is spent
|
|
1266
|
-
// in seconds, so a banned account that merely cooled stayed in the plan
|
|
1267
|
-
// and burned sibling accounts that were still healthy before the window
|
|
1268
|
-
// expired.
|
|
1269
|
-
//
|
|
1270
|
-
// The two shapes are told apart by the body, not the code alone: `11140`
|
|
1271
|
-
// names a restriction on the account, `11-128` refuses the request.
|
|
1272
|
-
if (s.buddyBanned) {
|
|
1273
|
-
return s.result(
|
|
1274
|
-
"policy_blocked_banned",
|
|
1275
|
-
"disabled",
|
|
1276
|
-
0,
|
|
1277
|
-
null,
|
|
1278
|
-
s.reason("Account banned by upstream"),
|
|
1279
|
-
// A ban is evidence about the credential even at provider scope, for
|
|
1280
|
-
// the same reason a named revocation is: the message is the evidence.
|
|
1281
|
-
// Without this a stream frame's missing credential detail would leave
|
|
1282
|
-
// a banned account serving.
|
|
1283
|
-
true,
|
|
1284
|
-
);
|
|
1285
|
-
}
|
|
1286
|
-
// Not a ban: the upstream refused this request. The account is fine, so
|
|
1287
|
-
// the only correct move is to stop retrying it verbatim. Cooling the
|
|
1288
|
-
// account here was the bug that made one malformed request look like a
|
|
1289
|
-
// dead credential.
|
|
1290
|
-
return s.result(
|
|
1291
|
-
"policy_blocked_invalid_request",
|
|
1292
|
-
"cooldown",
|
|
1293
|
-
0,
|
|
1294
|
-
null,
|
|
1295
|
-
s.reason("Upstream refused this request"),
|
|
1296
|
-
// Never mutates: a malformed body fails identically on every account,
|
|
1297
|
-
// so cooling would burn a healthy credential on the gateway's own fault.
|
|
1298
|
-
false,
|
|
1299
|
-
);
|
|
1300
|
-
},
|
|
1301
|
-
},
|
|
1302
|
-
{
|
|
1303
|
-
name: "request-scoped-rejection",
|
|
1304
|
-
match: (s) => s.requestScoped && s.buddy,
|
|
1305
|
-
decide: (s) =>
|
|
1306
|
-
s.result("unknown", "cooldown", 0, null, s.reason("Request-scoped upstream rejection"), false),
|
|
1307
|
-
},
|
|
1308
|
-
{
|
|
1309
|
-
name: "channel-rejection",
|
|
1310
|
-
match: (s) => s.channelRejection && s.buddy,
|
|
1311
|
-
decide: (s) => {
|
|
1312
|
-
const channelBlockMs = resolveAccountPolicyBlockCooldownMs();
|
|
1313
|
-
return s.result(
|
|
1314
|
-
"policy_blocked",
|
|
1315
|
-
"cooldown",
|
|
1316
|
-
channelBlockMs,
|
|
1317
|
-
new Date(Date.now() + channelBlockMs),
|
|
1318
|
-
s.reason("Provider channel rejection"),
|
|
1319
|
-
s.origin === "upstream",
|
|
1320
|
-
);
|
|
1321
|
-
},
|
|
1322
|
-
},
|
|
1323
1249
|
{
|
|
1324
1250
|
name: "validation-required",
|
|
1325
1251
|
match: (s) => s.validationRequired,
|
|
@@ -1468,6 +1394,81 @@ const ACCOUNT_ERROR_RULES: readonly AccountErrorRule[] = [
|
|
|
1468
1394
|
);
|
|
1469
1395
|
},
|
|
1470
1396
|
},
|
|
1397
|
+
{
|
|
1398
|
+
// Trae answers `4013 "AI service is not available in the current region
|
|
1399
|
+
// now."` when the upstream does not serve the account's region. That is a
|
|
1400
|
+
// durable verdict on the account, not a transient transport fault: it
|
|
1401
|
+
// reproduces on every request until the region opens, so it must cool hard
|
|
1402
|
+
// for a day rather than be re-dialled every thirty seconds. Must run
|
|
1403
|
+
// before `transport-fault`, which would read the 502 as a short
|
|
1404
|
+
// `server_error`.
|
|
1405
|
+
name: "trae-region-unavailable",
|
|
1406
|
+
match: (s) =>
|
|
1407
|
+
(s.providerId === "trae" || s.providerId === "traecn") &&
|
|
1408
|
+
(s.providerCode === "4013" || s.lower.includes("not available in the current region")),
|
|
1409
|
+
decide: (s) => {
|
|
1410
|
+
const cooldownMs = TRAE_REGION_COOLDOWN_MS;
|
|
1411
|
+
return s.result(
|
|
1412
|
+
"quota_exhausted",
|
|
1413
|
+
"cooldown",
|
|
1414
|
+
cooldownMs,
|
|
1415
|
+
new Date(Date.now() + cooldownMs),
|
|
1416
|
+
s.reason("Trae service unavailable in the current region"),
|
|
1417
|
+
// Account-scoped provider evidence still has to reach the writer: a
|
|
1418
|
+
// stream frame carries no credential detail, so `canMutate` is false
|
|
1419
|
+
// and without this the account stays in rotation to reproduce the
|
|
1420
|
+
// refusal for the whole window the region is closed for.
|
|
1421
|
+
true,
|
|
1422
|
+
);
|
|
1423
|
+
},
|
|
1424
|
+
},
|
|
1425
|
+
{
|
|
1426
|
+
// Trae's free-tier quota (`4008` / "exceeded the quota") resets daily.
|
|
1427
|
+
// Treating it like the generic quota cooldown (1h account-wide fallback)
|
|
1428
|
+
// let the account re-enter rotation inside the window and reproduce the
|
|
1429
|
+
// refusal every few minutes — the "cooldown not holding" report. The
|
|
1430
|
+
// verdict is a full-day backoff on *any* model (the account's quota is
|
|
1431
|
+
// spent, so every model on it shares the wall). Scoped to the model so
|
|
1432
|
+
// the account is not disabled wholesale; the 1-day window means a model
|
|
1433
|
+
// stays parked for the day instead of flapping. Must run before
|
|
1434
|
+
// `quota-exhausted`, which would apply the generic shorter fallback.
|
|
1435
|
+
name: "trae-quota-exhausted",
|
|
1436
|
+
match: (s) =>
|
|
1437
|
+
(s.providerId === "trae" || s.providerId === "traecn") &&
|
|
1438
|
+
(s.providerCode === "4008" || s.lower.includes("have exceeded the quota")),
|
|
1439
|
+
decide: (s) => {
|
|
1440
|
+
const cooldownMs = s.statedCooldownMs ?? TRAE_QUOTA_COOLDOWN_MS;
|
|
1441
|
+
return s.result(
|
|
1442
|
+
"model_unreachable",
|
|
1443
|
+
"cooldown",
|
|
1444
|
+
cooldownMs,
|
|
1445
|
+
new Date(Date.now() + cooldownMs),
|
|
1446
|
+
s.reason("Trae quota exhausted for this model"),
|
|
1447
|
+
s.origin === "upstream" && s.modelId !== undefined,
|
|
1448
|
+
);
|
|
1449
|
+
},
|
|
1450
|
+
},
|
|
1451
|
+
{
|
|
1452
|
+
// Qoder's quota verdicts — `110` "Billing daily count exceeded" and the
|
|
1453
|
+
// upstream-499 exhausted-quota close — are per-model: one model's spend
|
|
1454
|
+
// cap does not drain another. Scoping them to the model keeps the account
|
|
1455
|
+
// serving its other models. Must run before `quota-exhausted`, which
|
|
1456
|
+
// parks the whole account.
|
|
1457
|
+
name: "qoder-model-quota",
|
|
1458
|
+
match: (s) =>
|
|
1459
|
+
(s.providerId === "qoder" || s.providerId === "qodercn") && s.quotaSignal,
|
|
1460
|
+
decide: (s) => {
|
|
1461
|
+
const cooldownMs = s.statedCooldownMs ?? QODER_QUOTA_COOLDOWN_MS;
|
|
1462
|
+
return s.result(
|
|
1463
|
+
"model_unreachable",
|
|
1464
|
+
"cooldown",
|
|
1465
|
+
cooldownMs,
|
|
1466
|
+
new Date(Date.now() + cooldownMs),
|
|
1467
|
+
s.reason("Qoder quota exhausted for this model"),
|
|
1468
|
+
s.origin === "upstream" && s.modelId !== undefined,
|
|
1469
|
+
);
|
|
1470
|
+
},
|
|
1471
|
+
},
|
|
1471
1472
|
{
|
|
1472
1473
|
name: "quota-exhausted",
|
|
1473
1474
|
match: (s) => s.quotaSignal,
|
|
@@ -1478,11 +1479,21 @@ const ACCOUNT_ERROR_RULES: readonly AccountErrorRule[] = [
|
|
|
1478
1479
|
const grokFreeTier =
|
|
1479
1480
|
s.providerCode === "subscription:free-usage-exhausted" ||
|
|
1480
1481
|
s.providerCode.startsWith("subscription:") ||
|
|
1482
|
+
// A 402 spend/entitlement wall (`personal-team-blocked:spending-limit`,
|
|
1483
|
+
// "out of credits") is the same verdict: the account cannot serve until
|
|
1484
|
+
// the operator tops up or upgrades. The default 1h fallback re-dialled
|
|
1485
|
+
// it every hour inside a window that will not clear on its own.
|
|
1486
|
+
s.providerCode.includes("spending") ||
|
|
1487
|
+
s.providerCode.includes("credits") ||
|
|
1481
1488
|
s.lower.includes("free-usage") ||
|
|
1482
1489
|
s.lower.includes("free usage") ||
|
|
1483
1490
|
s.lower.includes("included free usage") ||
|
|
1484
1491
|
s.lower.includes("rolling 24-hour") ||
|
|
1485
|
-
s.lower.includes("rolling 24 hour")
|
|
1492
|
+
s.lower.includes("rolling 24 hour") ||
|
|
1493
|
+
s.lower.includes("spending limit") ||
|
|
1494
|
+
s.lower.includes("out of credits") ||
|
|
1495
|
+
s.lower.includes("run out of credits") ||
|
|
1496
|
+
s.lower.includes("need a grok subscription");
|
|
1486
1497
|
const buddyCreditsExhausted =
|
|
1487
1498
|
s.providerCode === "14018" || s.lower.includes("credits exhausted");
|
|
1488
1499
|
const cooldownMs = grokFreeTier
|
|
@@ -1631,20 +1642,24 @@ const ACCOUNT_ERROR_RULES: readonly AccountErrorRule[] = [
|
|
|
1631
1642
|
// verdict, not a transport fault. It must run before `transport-fault`,
|
|
1632
1643
|
// which otherwise reads the status as a 5xx-class outage and cools the
|
|
1633
1644
|
// account for the shorter transient window — or leaves it unmutated.
|
|
1645
|
+
//
|
|
1646
|
+
// The verdict is per-model, like Qoder's other quota signs: one model's
|
|
1647
|
+
// spend cap does not drain another, so it cools the pair, never the whole
|
|
1648
|
+
// account.
|
|
1634
1649
|
name: "qoder-499-quota",
|
|
1635
1650
|
match: (s) =>
|
|
1636
1651
|
(s.providerId === "qoder" || s.providerId === "qodercn") &&
|
|
1637
1652
|
s.statusCode === 499 &&
|
|
1638
1653
|
s.origin === "upstream",
|
|
1639
1654
|
decide: (s) => {
|
|
1640
|
-
const cooldownMs = s.statedCooldownMs ??
|
|
1655
|
+
const cooldownMs = s.statedCooldownMs ?? QODER_QUOTA_COOLDOWN_MS;
|
|
1641
1656
|
return s.result(
|
|
1642
|
-
"
|
|
1657
|
+
"model_unreachable",
|
|
1643
1658
|
"cooldown",
|
|
1644
1659
|
cooldownMs,
|
|
1645
1660
|
new Date(Date.now() + cooldownMs),
|
|
1646
|
-
s.reason("Qoder quota exhausted (upstream 499)"),
|
|
1647
|
-
s.origin === "upstream",
|
|
1661
|
+
s.reason("Qoder quota exhausted for this model (upstream 499)"),
|
|
1662
|
+
s.origin === "upstream" && s.modelId !== undefined,
|
|
1648
1663
|
);
|
|
1649
1664
|
},
|
|
1650
1665
|
},
|
|
@@ -34,6 +34,30 @@ export interface AccountHealthEventRecord {
|
|
|
34
34
|
readonly createdAt: string;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
/**
|
|
38
|
+
* Runs a write against the db handle, wrapping it in a transaction when the
|
|
39
|
+
* handle supports one. Kept as a capability probe, not a correctness guard:
|
|
40
|
+
* health writes are reachable with a partially-implemented database handle
|
|
41
|
+
* (the dispatch tests pass a narrow double), and a transaction is an
|
|
42
|
+
* optimization here, not a requirement — the same writes are correct without
|
|
43
|
+
* one. Returns `fallback` when the write throws; health must never fail
|
|
44
|
+
* requests.
|
|
45
|
+
*/
|
|
46
|
+
async function withTransaction<T>(
|
|
47
|
+
db: CartethyiaDatabase,
|
|
48
|
+
mutate: (tx: CartethyiaDatabase) => Promise<T>,
|
|
49
|
+
fallback: T,
|
|
50
|
+
): Promise<T> {
|
|
51
|
+
try {
|
|
52
|
+
if (typeof db.transaction === "function") {
|
|
53
|
+
return await db.transaction((tx) => mutate(tx as CartethyiaDatabase));
|
|
54
|
+
}
|
|
55
|
+
return await mutate(db);
|
|
56
|
+
} catch {
|
|
57
|
+
return fallback;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
37
61
|
/** Persists one classified failure to the account row and appends a health event. */
|
|
38
62
|
export async function recordAccountFailure(
|
|
39
63
|
db: CartethyiaDatabase,
|
|
@@ -260,18 +284,7 @@ export async function recordAccountFailure(
|
|
|
260
284
|
return { ...classification, routesVisible: true };
|
|
261
285
|
};
|
|
262
286
|
|
|
263
|
-
|
|
264
|
-
// Kept as a capability probe, not a correctness guard: this function is
|
|
265
|
-
// reachable with a partially-implemented database handle (the dispatch
|
|
266
|
-
// tests pass a narrow double), and a transaction is an optimization here,
|
|
267
|
-
// not a requirement — the same writes are correct without one.
|
|
268
|
-
if (typeof db.transaction === "function") {
|
|
269
|
-
return await db.transaction((tx) => persist(tx as CartethyiaDatabase));
|
|
270
|
-
}
|
|
271
|
-
return await persist(db);
|
|
272
|
-
} catch {
|
|
273
|
-
return null;
|
|
274
|
-
}
|
|
287
|
+
return withTransaction(db, persist, null);
|
|
275
288
|
}
|
|
276
289
|
|
|
277
290
|
/** Clears failure state after a successful dispatch; true when the account changed. */
|
|
@@ -333,14 +346,7 @@ export async function recordAccountSuccess(
|
|
|
333
346
|
}
|
|
334
347
|
return hadFailures;
|
|
335
348
|
};
|
|
336
|
-
|
|
337
|
-
if (typeof db.transaction === "function") {
|
|
338
|
-
return await db.transaction((tx) => mutate(tx as CartethyiaDatabase));
|
|
339
|
-
}
|
|
340
|
-
return await mutate(db);
|
|
341
|
-
} catch {
|
|
342
|
-
return false;
|
|
343
|
-
}
|
|
349
|
+
return withTransaction(db, mutate, false);
|
|
344
350
|
}
|
|
345
351
|
|
|
346
352
|
/** Operator-initiated recovery: forces the account back to active and logs the transition. */
|
|
@@ -393,14 +399,7 @@ export async function recoverAccount(
|
|
|
393
399
|
}
|
|
394
400
|
return true;
|
|
395
401
|
};
|
|
396
|
-
|
|
397
|
-
if (typeof db.transaction === "function") {
|
|
398
|
-
return await db.transaction((tx) => recover(tx as CartethyiaDatabase));
|
|
399
|
-
}
|
|
400
|
-
return await recover(db);
|
|
401
|
-
} catch {
|
|
402
|
-
return false;
|
|
403
|
-
}
|
|
402
|
+
return withTransaction(db, recover, false);
|
|
404
403
|
}
|
|
405
404
|
|
|
406
405
|
/** Recovers accounts whose cooldown elapsed and prunes expired per-model keys. */
|
|
@@ -481,14 +480,7 @@ export async function sweepExpiredCooldowns(db: CartethyiaDatabase): Promise<num
|
|
|
481
480
|
.where(pruning.changed);
|
|
482
481
|
return expired.length + affectedRows(pruned);
|
|
483
482
|
};
|
|
484
|
-
|
|
485
|
-
if (typeof db.transaction === "function") {
|
|
486
|
-
return await db.transaction((tx) => recover(tx as CartethyiaDatabase));
|
|
487
|
-
}
|
|
488
|
-
return await recover(db);
|
|
489
|
-
} catch {
|
|
490
|
-
return 0;
|
|
491
|
-
}
|
|
483
|
+
return withTransaction(db, recover, 0);
|
|
492
484
|
}
|
|
493
485
|
|
|
494
486
|
/**
|
|
@@ -519,8 +511,8 @@ async function writeHealthEvent(
|
|
|
519
511
|
row: {
|
|
520
512
|
readonly entityKind: "account";
|
|
521
513
|
readonly accountId: string;
|
|
522
|
-
readonly fromStatus:
|
|
523
|
-
readonly toStatus:
|
|
514
|
+
readonly fromStatus: "active" | "cooldown" | "disabled" | null;
|
|
515
|
+
readonly toStatus: "active" | "cooldown" | "disabled";
|
|
524
516
|
readonly reason: string;
|
|
525
517
|
readonly errorCategory: string | null;
|
|
526
518
|
readonly modelId?: string | null;
|
|
@@ -533,7 +525,7 @@ async function writeHealthEvent(
|
|
|
533
525
|
return;
|
|
534
526
|
}
|
|
535
527
|
if (typeof client.insert !== "function") return;
|
|
536
|
-
await client.insert(healthEvents).values({ id: randomUUID(), ...row }
|
|
528
|
+
await client.insert(healthEvents).values({ id: randomUUID(), ...row });
|
|
537
529
|
}
|
|
538
530
|
|
|
539
531
|
export async function reportAttemptOutcome(
|