cartethyia 2.7.0 → 2.7.2
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 +107 -106
- package/README.md +25 -2
- package/dist/dashboard/assets/{BannedPage-P76QLGJA.js → BannedPage-XaUmiBrC.js} +1 -1
- package/dist/dashboard/assets/BannedPage-XaUmiBrC.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolDetailPage-DdBggJS9.js → CliToolDetailPage-CqD7QiLM.js} +1 -1
- package/dist/dashboard/assets/CliToolDetailPage-CqD7QiLM.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolsPage-aN1iXhhP.js → CliToolsPage-0XUo4UMn.js} +1 -1
- package/dist/dashboard/assets/CliToolsPage-0XUo4UMn.js.gz +0 -0
- package/dist/dashboard/assets/{CombosPage-DE-F1nxZ.js → CombosPage-CXGS_Ct-.js} +1 -1
- package/dist/dashboard/assets/CombosPage-CXGS_Ct-.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-DGnGNYnM.js +1 -0
- package/dist/dashboard/assets/ConsoleLogPage-DGnGNYnM.js.gz +0 -0
- package/dist/dashboard/assets/{CustomizationPage-3CUxMy2X.js → CustomizationPage-DAG5J9EY.js} +1 -1
- package/dist/dashboard/assets/CustomizationPage-DAG5J9EY.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-BKtAPYVF.js +1 -0
- package/dist/dashboard/assets/HealthEventsModal-BKtAPYVF.js.gz +0 -0
- package/dist/dashboard/assets/LoginPage-BdmRLnq2.js +1 -0
- package/dist/dashboard/assets/LoginPage-BdmRLnq2.js.gz +0 -0
- package/dist/dashboard/assets/{ModelPicker-DKVslqG3.js → ModelPicker-BH8Tehdi.js} +1 -1
- package/dist/dashboard/assets/ModelPicker-BH8Tehdi.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-CqMxibJB.js +1 -0
- package/dist/dashboard/assets/OverviewPage-CqMxibJB.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-CqMh2NRX.js +4 -0
- package/dist/dashboard/assets/ProviderDetailPage-CqMh2NRX.js.gz +0 -0
- package/dist/dashboard/assets/ProviderIcon-FB6yS4v1.js +1 -0
- package/dist/dashboard/assets/ProviderIcon-FB6yS4v1.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-B9uWu4Nz.js +1 -0
- package/dist/dashboard/assets/ProvidersPage-B9uWu4Nz.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-BoWOhpDO.js +9 -0
- package/dist/dashboard/assets/ProxyPage-BoWOhpDO.js.gz +0 -0
- package/dist/dashboard/assets/{QuotaPage-BjldQwIU.js → QuotaPage-7P2n--Fv.js} +1 -1
- package/dist/dashboard/assets/QuotaPage-7P2n--Fv.js.gz +0 -0
- package/dist/dashboard/assets/{SettingsPage-DUIoLRxk.js → SettingsPage-CxZ1fTkN.js} +1 -1
- package/dist/dashboard/assets/SettingsPage-CxZ1fTkN.js.gz +0 -0
- package/dist/dashboard/assets/SetupPage-Co12Zzaw.js +1 -0
- package/dist/dashboard/assets/SetupPage-Co12Zzaw.js.gz +0 -0
- package/dist/dashboard/assets/{SortableList-fI7JkdIB.js → SortableList-Dqf4zpS-.js} +1 -1
- package/dist/dashboard/assets/SortableList-Dqf4zpS-.js.gz +0 -0
- package/dist/dashboard/assets/{StudioPage-CHVZqskZ.js → StudioPage-Byj2lAEm.js} +3 -3
- package/dist/dashboard/assets/StudioPage-Byj2lAEm.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-CTkLAuRc.js +56 -0
- package/dist/dashboard/assets/UsagePage-CTkLAuRc.js.gz +0 -0
- package/dist/dashboard/assets/{api-keys-DFY_fZU4.js → api-keys-CUi6UXsR.js} +1 -1
- package/dist/dashboard/assets/api-keys-CUi6UXsR.js.gz +0 -0
- package/dist/dashboard/assets/app-KvsUJUMF.css +2 -0
- package/dist/dashboard/assets/app-KvsUJUMF.css.gz +0 -0
- package/dist/dashboard/assets/{assert-CwdnZARt.js → assert-67xXH5rf.js} +1 -1
- package/dist/dashboard/assets/assert-67xXH5rf.js.gz +0 -0
- package/dist/dashboard/assets/{badge-Dr-uf0Au.js → badge-DC2L43Mj.js} +1 -1
- package/dist/dashboard/assets/badge-DC2L43Mj.js.gz +0 -0
- package/dist/dashboard/assets/{banner-BddmMdoS.js → banner-BBVcb0J3.js} +1 -1
- package/dist/dashboard/assets/banner-BBVcb0J3.js.gz +0 -0
- package/dist/dashboard/assets/{card-CK0PXWlX.js → card-Cca1XC6G.js} +1 -1
- package/dist/dashboard/assets/card-Cca1XC6G.js.gz +0 -0
- package/dist/dashboard/assets/{cli-tools-CcB6mGh2.js → cli-tools-KvsWa1Jl.js} +1 -1
- package/dist/dashboard/assets/cli-tools-KvsWa1Jl.js.gz +0 -0
- package/dist/dashboard/assets/{clipboard-button-DGb2P9Yx.js → clipboard-button-DUzEbynq.js} +1 -1
- package/dist/dashboard/assets/clipboard-button-DUzEbynq.js.gz +0 -0
- package/dist/dashboard/assets/{dialog-LkfuL50u.js → dialog-DFvWhwTa.js} +1 -1
- package/dist/dashboard/assets/dialog-DFvWhwTa.js.gz +0 -0
- package/dist/dashboard/assets/{drawer-ZbEZ_84c.js → drawer-CFM2sMX1.js} +1 -1
- package/dist/dashboard/assets/drawer-CFM2sMX1.js.gz +0 -0
- package/dist/dashboard/assets/{entry-BJfsVMvA.js → entry-Bqy8RFvM.js} +1 -1
- package/dist/dashboard/assets/entry-Bqy8RFvM.js.gz +0 -0
- package/dist/dashboard/assets/{entry-CWBY5A0f.js → entry-ClCMCWWQ.js} +3 -3
- package/dist/dashboard/assets/entry-ClCMCWWQ.js.gz +0 -0
- package/dist/dashboard/assets/{entry-CvC1-Zqx.js → entry-_xHo5x6r.js} +1 -1
- package/dist/dashboard/assets/entry-_xHo5x6r.js.gz +0 -0
- package/dist/dashboard/assets/formatters-CWWZ-uCZ.js +1 -0
- package/dist/dashboard/assets/formatters-CWWZ-uCZ.js.gz +0 -0
- package/dist/dashboard/assets/{github-badge-BxpznFLt.js → github-badge-C9e8Ts7e.js} +1 -1
- package/dist/dashboard/assets/github-badge-C9e8Ts7e.js.gz +0 -0
- package/dist/dashboard/assets/{index-CDzP3ovJ.js → index-DFVFrnWW.js} +2 -2
- package/dist/dashboard/assets/index-DFVFrnWW.js.gz +0 -0
- package/dist/dashboard/assets/{inline-Ae13MQ5w.js → inline-BFUcvllj.js} +1 -1
- package/dist/dashboard/assets/inline-BFUcvllj.js.gz +0 -0
- package/dist/dashboard/assets/{input-D23fo7cp.js → input-C_ILAvBb.js} +1 -1
- package/dist/dashboard/assets/input-C_ILAvBb.js.gz +0 -0
- package/dist/dashboard/assets/{layout-CSsxjj5P.js → layout-CBIyT6Rt.js} +1 -1
- package/dist/dashboard/assets/layout-CBIyT6Rt.js.gz +0 -0
- package/dist/dashboard/assets/{list-row-dmbwZMDY.js → list-row-ebJ_-Nsb.js} +1 -1
- package/dist/dashboard/assets/list-row-ebJ_-Nsb.js.gz +0 -0
- package/dist/dashboard/assets/{live-BG1oUsZZ.js → live-D0mm--ME.js} +1 -1
- package/dist/dashboard/assets/live-D0mm--ME.js.gz +0 -0
- package/dist/dashboard/assets/{mutation-DSF-e4ho.js → mutation-sMD965oO.js} +1 -1
- package/dist/dashboard/assets/mutation-sMD965oO.js.gz +0 -0
- package/dist/dashboard/assets/{numbers-Bm_aECY7.js → numbers-BO9dKaFr.js} +1 -1
- package/dist/dashboard/assets/numbers-BO9dKaFr.js.gz +0 -0
- package/dist/dashboard/assets/providers/inception.svg +5 -0
- package/dist/dashboard/assets/providers/inception.svg.gz +0 -0
- package/dist/dashboard/assets/{query-keys-CF56nYIV.js → query-keys-CfL_Zdoh.js} +1 -1
- package/dist/dashboard/assets/query-keys-CfL_Zdoh.js.gz +0 -0
- package/dist/dashboard/assets/{routing-CeA71n7G.js → routing-BnIpi1IJ.js} +1 -1
- package/dist/dashboard/assets/routing-BnIpi1IJ.js.gz +0 -0
- package/dist/dashboard/assets/{select-KIT0rC6G.js → select-DBNVLdUu.js} +1 -1
- package/dist/dashboard/assets/select-DBNVLdUu.js.gz +0 -0
- package/dist/dashboard/assets/{settings-B_2gkePf.js → settings-BAFhDgX8.js} +1 -1
- package/dist/dashboard/assets/settings-BAFhDgX8.js.gz +0 -0
- package/dist/dashboard/assets/{stack-D7WOSzcX.js → stack-EMrOMt4I.js} +1 -1
- package/dist/dashboard/assets/stack-EMrOMt4I.js.gz +0 -0
- package/dist/dashboard/assets/{state-BBxtKZOQ.js → state-CWoo7ty5.js} +1 -1
- package/dist/dashboard/assets/state-CWoo7ty5.js.gz +0 -0
- package/dist/dashboard/assets/{switch-DNS_D5Ea.js → switch-CCjT6IkF.js} +1 -1
- package/dist/dashboard/assets/switch-CCjT6IkF.js.gz +0 -0
- package/dist/dashboard/assets/{useMutation-BgoxdoMk.js → useMutation-DjkLHUcE.js} +1 -1
- package/dist/dashboard/assets/useMutation-DjkLHUcE.js.gz +0 -0
- package/dist/dashboard/index.html +1 -1
- package/dist/dashboard/index.html.gz +0 -0
- package/dist/dashboard/precompressed-assets.json +54 -53
- package/migrations/full-postgres/0024_add_pool_provider_cooldowns.sql +12 -0
- package/migrations/full-postgres/0025_add_pool_random_strategy.sql +3 -0
- package/migrations/lite-sqlite3/0024_add_pool_provider_cooldowns.sql +12 -0
- package/migrations/lite-sqlite3/0025_add_pool_random_strategy.sql +20 -0
- package/package.json +96 -96
- package/scripts/cli/console-menu.ts +5 -2
- package/scripts/cli/tray.ts +21 -6
- package/src/boot/app.ts +684 -714
- package/src/boot/dependencies.ts +65 -62
- package/src/config/cooldowns.ts +16 -0
- package/src/config/providers.ts +13 -1
- package/src/config/resources.ts +70 -70
- package/src/config/security.ts +37 -37
- package/src/config/spec.ts +371 -358
- package/src/console/cli-tools/contracts.ts +10 -4
- package/src/console/console-router.ts +233 -225
- package/src/console/domain-registration.ts +398 -381
- package/src/console/providers/catalog/catalog-projections.ts +2 -6
- package/src/console/providers/catalog/contracts.ts +19 -0
- package/src/console/providers/catalog/provider-operations.ts +38 -0
- package/src/console/providers/catalog/routes.ts +14 -0
- package/src/console/providers/catalog/store-helpers.ts +326 -0
- package/src/console/providers/catalog/store.ts +1292 -1563
- package/src/console/quota/account-quota-global.ts +2 -2
- package/src/console/quota/account-quota-shared.ts +1 -1
- package/src/console/quota/account-quota-tenant.ts +35 -12
- package/src/console/quota/quota-view.ts +2 -2
- package/src/console/routing/pools/contracts.ts +41 -1
- package/src/console/routing/pools/operations.ts +183 -4
- package/src/console/routing/pools/relay-deploy.ts +17 -4
- package/src/console/routing/pools/routes.ts +30 -0
- package/src/console/routing/pools/store.ts +98 -8
- package/src/main.ts +183 -167
- package/src/network/outbound-fetch.ts +124 -208
- package/src/network/pool/agent.ts +15 -0
- package/src/network/pool/pool-state.ts +7 -0
- package/src/network/pool/selector.ts +110 -2
- package/src/network/pool-cooldown-store.ts +252 -0
- package/src/network/pool-health-machine.ts +5 -3
- package/src/network/pool-health.ts +36 -9
- package/src/observability/event-query.ts +57 -0
- package/src/observability/metrics.ts +467 -467
- package/src/observability/runtime-metrics.ts +269 -269
- package/src/persistence/health-event-flush.ts +2 -0
- package/src/persistence/schema.ts +19 -0
- package/src/persistence/usage-flush.ts +8 -3
- package/src/protocol/canonical-model.ts +1 -1
- package/src/protocol/dsml.ts +377 -0
- package/src/protocol/gateway-error.ts +232 -232
- package/src/{providers/provider-reasoning.ts → protocol/reasoning.ts} +10 -9
- package/src/protocol/request/chat.ts +7 -7
- package/src/protocol/request/codex.ts +2 -2
- package/src/protocol/request/gemini.ts +1 -1
- package/src/protocol/request/messages.ts +6 -6
- package/src/protocol/request/responses.ts +7 -7
- package/src/protocol/response/dsml.ts +12 -133
- package/src/{transport → protocol}/translation/attachment-integrity.ts +2 -2
- package/src/{transport → protocol}/translation/cache-controls.ts +1 -1
- package/src/{transport → protocol}/translation/capabilities.ts +2 -2
- package/src/{transport → protocol}/translation/normalize.ts +1 -1
- package/src/{transport → protocol}/translation/quirks.ts +1 -1
- package/src/{transport → protocol}/translation/search.ts +1 -1
- package/src/{transport → protocol}/translation/thinking.ts +23 -1
- package/src/{transport → protocol}/translation/tool-id.ts +1 -1
- package/src/{transport → protocol}/translation/tool-repair.ts +2 -2
- package/src/protocol/upstream-parsing.ts +176 -0
- package/src/providers/adapters/compatible-adapter.ts +11 -2
- package/src/providers/authentication/oauth-refresh-service.ts +9 -5
- package/src/providers/default-registry.ts +22 -5
- package/src/providers/discovery/probe-phases.ts +16 -3
- package/src/providers/discovery/probing-service.ts +4 -1
- package/src/providers/integrations/anthropic.ts +1 -1
- package/src/providers/integrations/buddy/buddy-catalog-shared.ts +1 -4
- package/src/providers/integrations/buddy/buddy-chat-shared.ts +141 -8
- package/src/providers/integrations/buddy/buddy-checkin.ts +5 -1
- package/src/providers/integrations/buddy/buddy-growth-shared.ts +0 -82
- package/src/providers/integrations/buddy/buddy-identity.ts +25 -0
- package/src/providers/integrations/buddy/buddy-server-tools.ts +240 -0
- package/src/providers/integrations/buddy/codebuddy/cn-catalog.ts +36 -0
- package/src/providers/integrations/buddy/codebuddy/cn-provider.ts +6 -31
- package/src/providers/integrations/buddy/codebuddy/provider.ts +2 -0
- package/src/providers/integrations/buddy/codebuddy/shared.ts +19 -4
- package/src/providers/integrations/buddy/turing-device-token.ts +262 -0
- package/src/providers/integrations/buddy/workbuddy/provider.ts +2 -0
- package/src/providers/integrations/buddy/workbuddy/shared.ts +56 -3
- package/src/providers/integrations/claude/claude.ts +1 -1
- package/src/providers/integrations/cloudflareai/cloudflareai.ts +1 -1
- package/src/providers/integrations/codex/codex-errors.ts +1 -1
- package/src/providers/integrations/codex/codex.ts +1 -1
- package/src/providers/integrations/factory/factory.ts +1 -1
- package/src/providers/integrations/github/github.ts +1 -1
- package/src/providers/integrations/grok/grok.ts +1 -1
- package/src/providers/integrations/inception/provider.ts +91 -0
- package/src/providers/integrations/inferhub.ts +1 -1
- package/src/providers/integrations/kiro/kiro-request.ts +1 -1
- package/src/providers/integrations/mistral.ts +193 -19
- package/src/providers/integrations/ollamacloud-models.ts +50 -0
- package/src/providers/integrations/opencode/fingerprint.ts +19 -2
- package/src/providers/integrations/opencode/prompt.ts +111 -0
- package/src/providers/integrations/opencode/provider.ts +101 -23
- package/src/providers/integrations/opencode/tool-definitions.ts +4 -4
- package/src/providers/integrations/qoder/qoder-checkin.ts +303 -0
- package/src/providers/integrations/qoder/qoder-oauth.ts +12 -1
- package/src/providers/integrations/qoder/qoder-realm.ts +20 -0
- package/src/providers/integrations/qoder/qoder-text-tools.ts +277 -0
- package/src/providers/integrations/qoder/qoder.ts +315 -400
- package/src/providers/integrations/shared/install-id.ts +2 -1
- package/src/providers/integrations/trae/trae-cache.ts +110 -0
- package/src/providers/integrations/trae/trae-oauth.ts +29 -4
- package/src/providers/integrations/trae/trae-protocol.ts +2 -1
- package/src/providers/integrations/trae/trae.ts +106 -23
- package/src/providers/operations/account-error-classification.ts +1684 -0
- package/src/providers/operations/account-health-service.ts +27 -1503
- package/src/providers/operations/account-reset-service.ts +2 -0
- package/src/providers/operations/provider-catalog-service.ts +11 -1
- package/src/providers/provider-contracts.ts +7 -4
- package/src/providers/provider-metadata.ts +25 -4
- package/src/providers/provider-probe.ts +2 -2
- package/src/{console → providers}/quota/cache.ts +1 -1
- package/src/{console → providers}/quota/refresh.ts +5 -5
- package/src/security/cli-client-fingerprint.ts +9 -2
- package/src/transport/dispatch/attempt-finalize.ts +114 -12
- package/src/transport/dispatch/attempt-loop.ts +3 -0
- package/src/transport/dispatch/empty-stream.ts +9 -2
- package/src/transport/dispatch/generation.ts +37 -55
- package/src/transport/dispatch/handler-deps.ts +32 -0
- package/src/transport/dispatch/leases.ts +9 -4
- package/src/transport/dispatch/proxy-request.ts +6 -30
- package/src/transport/dispatch/responses-compact.ts +22 -43
- package/src/transport/dispatch/streaming/finalize.ts +3 -0
- package/src/transport/dispatch/streaming/stream.ts +0 -8
- package/src/transport/dispatch/systemone.ts +22 -42
- package/src/transport/dispatch/token-count.ts +23 -39
- package/src/transport/dispatch/upstream.ts +1 -3
- package/src/transport/dispatch/websearch.ts +22 -41
- package/src/transport/failure-policy.ts +92 -178
- package/src/transport/guards/abuse-guard.ts +10 -0
- package/src/transport/guards/auth-guard.ts +109 -109
- package/src/transport/guards/error-lifecycle.ts +1 -1
- package/src/transport/guards/pipeline.ts +220 -220
- package/src/transport/guards/request-context.ts +245 -238
- package/src/transport/request/degradation.ts +20 -1
- package/src/transport/request/inflight.ts +4 -114
- package/src/transport/request/preparer.ts +6 -6
- package/src/transport/request/state.ts +66 -38
- package/src/transport/request/tenant-preferences.ts +1 -1
- package/src/transport/routing/route-catalog.ts +75 -43
- package/src/transport/routing/router.ts +30 -2
- package/src/transport/surface/messages/parse.ts +1 -1
- package/src/transport/surface/responses/encode.ts +2 -3
- package/src/transport/surface/responses/parse.ts +49 -7
- package/src/types/undici.d.ts +33 -0
- package/src/workers/buddy-growth-tasks.ts +346 -425
- package/src/workers/daily-checkin.ts +17 -6
- package/src/workers/quota-refresh-sweep.ts +2 -2
- package/src/workers/scheduler.ts +17 -1
- package/dist/dashboard/assets/BannedPage-P76QLGJA.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-DdBggJS9.js.gz +0 -0
- package/dist/dashboard/assets/CliToolsPage-aN1iXhhP.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-DE-F1nxZ.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-5SpmfLyw.js +0 -1
- package/dist/dashboard/assets/ConsoleLogPage-5SpmfLyw.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-3CUxMy2X.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-BkmJQKC0.js +0 -1
- package/dist/dashboard/assets/HealthEventsModal-BkmJQKC0.js.gz +0 -0
- package/dist/dashboard/assets/LoginPage-B_0FUKWc.js +0 -1
- package/dist/dashboard/assets/LoginPage-B_0FUKWc.js.gz +0 -0
- package/dist/dashboard/assets/ModelPicker-DKVslqG3.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-D-H4MEiT.js +0 -1
- package/dist/dashboard/assets/OverviewPage-D-H4MEiT.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-CEJw6Guo.js +0 -3
- package/dist/dashboard/assets/ProviderDetailPage-CEJw6Guo.js.gz +0 -0
- package/dist/dashboard/assets/ProviderIcon-CmfzxpVM.js +0 -1
- package/dist/dashboard/assets/ProviderIcon-CmfzxpVM.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-CEplAR-i.js +0 -1
- package/dist/dashboard/assets/ProvidersPage-CEplAR-i.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-CAN7hy0f.js +0 -7
- package/dist/dashboard/assets/ProxyPage-CAN7hy0f.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-BjldQwIU.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-DUIoLRxk.js.gz +0 -0
- package/dist/dashboard/assets/SetupPage-_bCaTM0v.js +0 -1
- package/dist/dashboard/assets/SetupPage-_bCaTM0v.js.gz +0 -0
- package/dist/dashboard/assets/SortableList-fI7JkdIB.js.gz +0 -0
- package/dist/dashboard/assets/StudioPage-CHVZqskZ.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-DiFBymHj.js +0 -56
- package/dist/dashboard/assets/UsagePage-DiFBymHj.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-DFY_fZU4.js.gz +0 -0
- package/dist/dashboard/assets/app-BOJ1_xUB.css +0 -2
- package/dist/dashboard/assets/app-BOJ1_xUB.css.gz +0 -0
- package/dist/dashboard/assets/assert-CwdnZARt.js.gz +0 -0
- package/dist/dashboard/assets/badge-Dr-uf0Au.js.gz +0 -0
- package/dist/dashboard/assets/banner-BddmMdoS.js.gz +0 -0
- package/dist/dashboard/assets/card-CK0PXWlX.js.gz +0 -0
- package/dist/dashboard/assets/cli-tools-CcB6mGh2.js.gz +0 -0
- package/dist/dashboard/assets/clipboard-button-DGb2P9Yx.js.gz +0 -0
- package/dist/dashboard/assets/cog-CdWhsajs.js +0 -1
- package/dist/dashboard/assets/cog-CdWhsajs.js.gz +0 -0
- package/dist/dashboard/assets/dialog-LkfuL50u.js.gz +0 -0
- package/dist/dashboard/assets/drawer-ZbEZ_84c.js.gz +0 -0
- package/dist/dashboard/assets/entry-BJfsVMvA.js.gz +0 -0
- package/dist/dashboard/assets/entry-CWBY5A0f.js.gz +0 -0
- package/dist/dashboard/assets/entry-CvC1-Zqx.js.gz +0 -0
- package/dist/dashboard/assets/github-badge-BxpznFLt.js.gz +0 -0
- package/dist/dashboard/assets/index-CDzP3ovJ.js.gz +0 -0
- package/dist/dashboard/assets/inline-Ae13MQ5w.js.gz +0 -0
- package/dist/dashboard/assets/input-D23fo7cp.js.gz +0 -0
- package/dist/dashboard/assets/layout-CSsxjj5P.js.gz +0 -0
- package/dist/dashboard/assets/list-row-dmbwZMDY.js.gz +0 -0
- package/dist/dashboard/assets/live-BG1oUsZZ.js.gz +0 -0
- package/dist/dashboard/assets/mutation-DSF-e4ho.js.gz +0 -0
- package/dist/dashboard/assets/numbers-Bm_aECY7.js.gz +0 -0
- package/dist/dashboard/assets/query-keys-CF56nYIV.js.gz +0 -0
- package/dist/dashboard/assets/routing-CeA71n7G.js.gz +0 -0
- package/dist/dashboard/assets/select-KIT0rC6G.js.gz +0 -0
- package/dist/dashboard/assets/settings-B_2gkePf.js.gz +0 -0
- package/dist/dashboard/assets/stack-D7WOSzcX.js.gz +0 -0
- package/dist/dashboard/assets/state-BBxtKZOQ.js.gz +0 -0
- package/dist/dashboard/assets/switch-DNS_D5Ea.js.gz +0 -0
- package/dist/dashboard/assets/useMutation-BgoxdoMk.js.gz +0 -0
- /package/dist/dashboard/assets/{app-CaXZ9SCn.js → app-BaWd0puM.js} +0 -0
- /package/dist/dashboard/assets/{app-CaXZ9SCn.js.gz → app-BaWd0puM.js.gz} +0 -0
|
@@ -168,6 +168,8 @@ export interface ProbeTarget {
|
|
|
168
168
|
* so the probe tests the route, not an empty stand-in.
|
|
169
169
|
*/
|
|
170
170
|
readonly capabilityProfile: Readonly<Record<string, boolean>>;
|
|
171
|
+
/** The model row's output ceiling, when it declares one. */
|
|
172
|
+
readonly outputLimit?: number | null;
|
|
171
173
|
}
|
|
172
174
|
|
|
173
175
|
/**
|
|
@@ -217,10 +219,11 @@ export async function resolveProbeTarget(args: {
|
|
|
217
219
|
let serviceKind: ServiceKind = requestedServiceKind;
|
|
218
220
|
// Capability columns for the row the probe lands on. Resolved alongside the
|
|
219
221
|
// wire so the profile describes the same row the probe dispatches to.
|
|
220
|
-
let capabilitySource: { modalities: unknown; reasoning: boolean; toolCall: boolean } = {
|
|
222
|
+
let capabilitySource: { modalities: unknown; reasoning: boolean; toolCall: boolean; outputLimit: number | null } = {
|
|
221
223
|
modalities: null,
|
|
222
224
|
reasoning: false,
|
|
223
225
|
toolCall: false,
|
|
226
|
+
outputLimit: null,
|
|
224
227
|
};
|
|
225
228
|
if (request.wireFamily) {
|
|
226
229
|
wireFamily = request.wireFamily as WireFamily;
|
|
@@ -242,6 +245,7 @@ export async function resolveProbeTarget(args: {
|
|
|
242
245
|
modalities: staticDef.modalities,
|
|
243
246
|
reasoning: staticDef.reasoning,
|
|
244
247
|
toolCall: staticDef.toolCall,
|
|
248
|
+
outputLimit: staticDef.outputLimit ?? null,
|
|
245
249
|
};
|
|
246
250
|
} else {
|
|
247
251
|
const existing = await db
|
|
@@ -252,6 +256,7 @@ export async function resolveProbeTarget(args: {
|
|
|
252
256
|
modalities: models.modalities,
|
|
253
257
|
reasoning: models.reasoning,
|
|
254
258
|
toolCall: models.toolCall,
|
|
259
|
+
outputLimit: models.outputLimit,
|
|
255
260
|
})
|
|
256
261
|
.from(models)
|
|
257
262
|
.where(
|
|
@@ -277,6 +282,7 @@ export async function resolveProbeTarget(args: {
|
|
|
277
282
|
modalities: existing[0].modalities,
|
|
278
283
|
reasoning: existing[0].reasoning,
|
|
279
284
|
toolCall: existing[0].toolCall,
|
|
285
|
+
outputLimit: existing[0].outputLimit ?? null,
|
|
280
286
|
};
|
|
281
287
|
} else {
|
|
282
288
|
if (requestedServiceKind !== "llm") {
|
|
@@ -334,7 +340,7 @@ export async function resolveProbeTarget(args: {
|
|
|
334
340
|
toolCall: capabilitySource.toolCall,
|
|
335
341
|
providerId,
|
|
336
342
|
});
|
|
337
|
-
return { wireFamily, serviceKind, endpointPath, sourceSurface, capabilityProfile };
|
|
343
|
+
return { wireFamily, serviceKind, endpointPath, sourceSurface, capabilityProfile, outputLimit: capabilitySource.outputLimit };
|
|
338
344
|
}
|
|
339
345
|
|
|
340
346
|
/** The account a probe selected, or the operator-facing reason it could not. */
|
|
@@ -752,12 +758,19 @@ export function computeProbeVerdict(args: {
|
|
|
752
758
|
: status !== undefined
|
|
753
759
|
? `upstream ${status}`
|
|
754
760
|
: undefined;
|
|
761
|
+
// A 429 that names a free-tier usage limit is upstream quota state, not a
|
|
762
|
+
// payload or egress defect — label it as the throttle it is instead of a
|
|
763
|
+
// generic probe failure.
|
|
764
|
+
const throttled =
|
|
765
|
+
status === 429 ||
|
|
766
|
+
providerStatus === 429 ||
|
|
767
|
+
/free usage limit|rate limit|throttl/i.test(base);
|
|
755
768
|
// The toast title already names the model. Repeating `provider/model via
|
|
756
769
|
// wire path` in the body made the operator read the same identity twice
|
|
757
770
|
// and buried the upstream sentence. The wire family stays, because that is
|
|
758
771
|
// the one fact the title does not carry; the path is noise next to it.
|
|
759
772
|
const tagged = statusTag !== undefined ? `${pretty} (${statusTag})` : pretty;
|
|
760
|
-
errorMessage = `${wireFamily} — ${tagged}${upstreamReq ? ` [req ${upstreamReq}]` : ""}`;
|
|
773
|
+
errorMessage = `${wireFamily} — ${throttled ? "Upstream rate limit (HTTP 429): " : ""}${tagged}${upstreamReq ? ` [req ${upstreamReq}]` : ""}`;
|
|
761
774
|
|
|
762
775
|
} else if (terminal?.type === "terminal" && terminal.state !== "complete") {
|
|
763
776
|
// Read the terminal's own detail before falling back to the stop reason.
|
|
@@ -192,7 +192,7 @@ export class ProviderProbingService {
|
|
|
192
192
|
const providerWireRow = await this.loadProviderWireRow(providerId);
|
|
193
193
|
const requiresAccount = providerWireRow?.requiresAccount ?? true;
|
|
194
194
|
|
|
195
|
-
const { wireFamily, serviceKind, endpointPath, sourceSurface, capabilityProfile } = await resolveProbeTarget({
|
|
195
|
+
const { wireFamily, serviceKind, endpointPath, sourceSurface, capabilityProfile, outputLimit } = await resolveProbeTarget({
|
|
196
196
|
db: this.db,
|
|
197
197
|
bundledModelCatalog: this.bundledModelCatalog,
|
|
198
198
|
defaultEndpoints: this.defaultEndpoints,
|
|
@@ -253,6 +253,9 @@ export class ProviderProbingService {
|
|
|
253
253
|
// over capabilities the route actually has (`prompt_caching`, `tools`,
|
|
254
254
|
// `reasoning`), reporting a healthy provider as broken.
|
|
255
255
|
capabilities: capabilityProfile,
|
|
256
|
+
// The model row's own output ceiling, so a probe with a max-output
|
|
257
|
+
// ceiling (OpenCode free) shapes with the model's limit, not a guess.
|
|
258
|
+
...(outputLimit == null ? {} : { output_limit: outputLimit }),
|
|
256
259
|
};
|
|
257
260
|
|
|
258
261
|
const probeSignal = AbortSignal.timeout(30_000);
|
|
@@ -17,7 +17,7 @@ import type { CanonicalEvent, CanonicalRequest } from "../../protocol/canonical-
|
|
|
17
17
|
import { GatewayError } from "../../protocol/gateway-error";
|
|
18
18
|
import { buildClaudeMessagesRequest, filterClaudeCustomHeaders } from "./shared/claude-messages";
|
|
19
19
|
import { sendClaudeMessagesRequest } from "../../protocol/upstream/send-claude-messages";
|
|
20
|
-
import { applyParamQuirks } from "../../
|
|
20
|
+
import { applyParamQuirks } from "../../protocol/translation/quirks";
|
|
21
21
|
import type { ProviderDispatchTarget, ProviderAdapter, ProviderDispatchContext, ProviderId } from "../provider-contracts";
|
|
22
22
|
import { providerBaseUrl } from "../provider-metadata";
|
|
23
23
|
|
|
@@ -42,7 +42,6 @@ export const BUDDY_INTL_EFFORTS: Readonly<Record<string, readonly string[]>> = {
|
|
|
42
42
|
"glm-5.2": ["high", "xhigh"],
|
|
43
43
|
"glm-5.3": ["low", "high", "max"],
|
|
44
44
|
hy3: ["low", "high"],
|
|
45
|
-
"hy3-preview": ["low", "high"],
|
|
46
45
|
"hy4-preview-f": ["high"],
|
|
47
46
|
};
|
|
48
47
|
export const BUDDY_CN_EFFORTS: Readonly<Record<string, readonly string[]>> = {
|
|
@@ -54,7 +53,6 @@ export const BUDDY_CN_EFFORTS: Readonly<Record<string, readonly string[]>> = {
|
|
|
54
53
|
"glm-5.3": ["low", "high", "max"],
|
|
55
54
|
"glm-5.3-flash": ["low", "high", "max"],
|
|
56
55
|
hy3: ["low", "high"],
|
|
57
|
-
"hy3-preview": ["low", "high"],
|
|
58
56
|
"hy4-preview": ["high"],
|
|
59
57
|
};
|
|
60
58
|
|
|
@@ -336,7 +334,7 @@ export const BUDDY_SHARED_RAW: readonly BuddyRawEntry[] = [
|
|
|
336
334
|
["glm-5.1", "GLM-5.1", true, false, 200_000, 131_072],
|
|
337
335
|
["glm-5.2", "GLM-5.2", true, true, 1_000_000, 48_000],
|
|
338
336
|
["glm-5.3", "GLM-5.3", true, true, 1_000_000, 48_000],
|
|
339
|
-
["glm-5.3-flash", "GLM-5.3 Flash", true,
|
|
337
|
+
["glm-5.3-flash", "GLM-5.3 Flash", true, false, 1_000_000, 32_000],
|
|
340
338
|
["glm-5v-turbo", "GLM-5V Turbo", true, true, 200_000, 131_072],
|
|
341
339
|
["gpt-5.3-codex", "GPT-5.3 Codex", true, true, 400_000, 128_000],
|
|
342
340
|
["gpt-5.4", "GPT-5.4", true, true, 272_000, 72_000],
|
|
@@ -351,7 +349,6 @@ export const BUDDY_SHARED_RAW: readonly BuddyRawEntry[] = [
|
|
|
351
349
|
["grok-4.6", "Grok 4.6", true, true, 500_000, 500_000],
|
|
352
350
|
["grok-4.7", "Grok 4.7", true, true, 500_000, 128_000],
|
|
353
351
|
["hy3", "Hy3", true, true, 192_000, 64_000, true],
|
|
354
|
-
["hy3-preview", "Hy3 Preview", true, false, 262_144, 64_000],
|
|
355
352
|
["hy4-preview", "Hy4 Preview", true, true, 1_000_000, 64_000, true],
|
|
356
353
|
["hy4-preview-f", "Hy4 Preview F", true, true, 1_000_000, 64_000, true],
|
|
357
354
|
["kimi-k2.5", "Kimi K2.5", true, false, 164_000, 262_144],
|
|
@@ -21,14 +21,14 @@ import { createHash } from "node:crypto";
|
|
|
21
21
|
import { completeRequiredSchema } from "../../../protocol/codecs";
|
|
22
22
|
import type { CanonicalRequest } from "../../../protocol/canonical-model";
|
|
23
23
|
import { isRecord } from "../../../protocol/codecs";
|
|
24
|
-
import { applyDeepSeekReasoning, applyOpenAIReasoning, backfillDeepSeekReasoningContent, clampedEffortOutputFloor } from "
|
|
24
|
+
import { applyDeepSeekReasoning, applyOpenAIReasoning, backfillDeepSeekReasoningContent, clampedEffortOutputFloor } from "../../../protocol/reasoning";
|
|
25
25
|
import { BUDDY_CN_EFFORTS, BUDDY_INTL_EFFORTS, BUDDY_SHARED_RAW } from "./buddy-catalog-shared";
|
|
26
26
|
// Imported for the CN roster only: a CN-only id has no row in the shared
|
|
27
27
|
// table, and without one `raiseOutputForRaisedEffort` silently no-ops, so a
|
|
28
28
|
// raised effort still answers inside a budget sized for the lower one. It is
|
|
29
29
|
// read inside the function below — never at module init — so the cycle with
|
|
30
30
|
// cn-provider (which imports this module) resolves before any call.
|
|
31
|
-
import { CODEBUDDY_CN_RAW } from "./codebuddy/cn-
|
|
31
|
+
import { CODEBUDDY_CN_RAW } from "./codebuddy/cn-catalog";
|
|
32
32
|
import { buddyAccountUid } from "./buddy-oauth-shared";
|
|
33
33
|
import type { ProviderDispatchContext } from "../../provider-contracts";
|
|
34
34
|
|
|
@@ -987,13 +987,146 @@ export function ensureBuddyLeadingSystem(
|
|
|
987
987
|
* (`11151`), then guarantee a leading system turn (`11128`). The system text
|
|
988
988
|
* is the variant's own prompt, so each pre-payload hook passes its prompt in.
|
|
989
989
|
*/
|
|
990
|
-
|
|
990
|
+
/**
|
|
991
|
+
* Longest message text that gets an automatic ephemeral prompt-cache marker,
|
|
992
|
+
* mirroring the reference gateway (`MIN_AUTO_CACHE_TEXT_LENGTH`).
|
|
993
|
+
*/
|
|
994
|
+
const MIN_AUTO_CACHE_TEXT_LENGTH = 1024;
|
|
995
|
+
|
|
996
|
+
/**
|
|
997
|
+
* Marks one message's text content cacheable in place: the system turn and the
|
|
998
|
+
* last user turn are the long, stable ones a prefix cache serves best. A
|
|
999
|
+
* string message becomes a typed text block; a text part inside an array gets
|
|
1000
|
+
* `cache_control: {type: "ephemeral"}`. Only the first qualifying text part of
|
|
1001
|
+
* a message is marked, like the reference.
|
|
1002
|
+
*/
|
|
1003
|
+
function markCacheableTextContent(message: Record<string, unknown>): void {
|
|
1004
|
+
const content = message["content"];
|
|
1005
|
+
if (typeof content === "string") {
|
|
1006
|
+
if (content.trim().length >= MIN_AUTO_CACHE_TEXT_LENGTH) {
|
|
1007
|
+
message["content"] = [
|
|
1008
|
+
{ type: "text", text: content, cache_control: { type: "ephemeral" } },
|
|
1009
|
+
];
|
|
1010
|
+
}
|
|
1011
|
+
return;
|
|
1012
|
+
}
|
|
1013
|
+
if (!Array.isArray(content)) return;
|
|
1014
|
+
for (let index = 0; index < content.length; index += 1) {
|
|
1015
|
+
const part = content[index];
|
|
1016
|
+
if (!isRecord(part) || part["type"] !== "text") continue;
|
|
1017
|
+
const text = typeof part["text"] === "string" ? part["text"] : "";
|
|
1018
|
+
if (text.trim().length >= MIN_AUTO_CACHE_TEXT_LENGTH && part["cache_control"] === undefined) {
|
|
1019
|
+
content[index] = { ...part, cache_control: { type: "ephemeral" } };
|
|
1020
|
+
return;
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
/**
|
|
1026
|
+
* Auto-injects ephemeral prompt-cache markers on the system turn and the last
|
|
1027
|
+
* user turn when the caller declared none. Mirrors `applyPromptCacheControl` in
|
|
1028
|
+
* the reference gateway: an explicit `cache_control` anywhere is respected and
|
|
1029
|
+
* disables the auto-pass so the caller's own caching strategy wins.
|
|
1030
|
+
*/
|
|
1031
|
+
export function applyBuddyAutoPromptCache(messages: Array<Record<string, unknown>>): void {
|
|
1032
|
+
const hasExplicit = messages.some((message) =>
|
|
1033
|
+
Array.isArray(message["content"]) &&
|
|
1034
|
+
(message["content"] as Array<Record<string, unknown>>).some(
|
|
1035
|
+
(part) => isRecord(part) && part["cache_control"] !== undefined,
|
|
1036
|
+
),
|
|
1037
|
+
);
|
|
1038
|
+
if (hasExplicit) return;
|
|
1039
|
+
if (messages.length === 0) return;
|
|
1040
|
+
const first = messages[0];
|
|
1041
|
+
if (isRecord(first) && first["role"] === "system") markCacheableTextContent(first);
|
|
1042
|
+
let lastUser = -1;
|
|
1043
|
+
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
1044
|
+
if (isRecord(messages[index]) && messages[index]!["role"] === "user") {
|
|
1045
|
+
lastUser = index;
|
|
1046
|
+
break;
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
if (lastUser >= 0) markCacheableTextContent(messages[lastUser]!);
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
/**
|
|
1053
|
+
* Strips the `content` key from assistant tool_calls-only turns.
|
|
1054
|
+
*
|
|
1055
|
+
* Mirrors the reference gateway's OpenAI transcript: an assistant turn that
|
|
1056
|
+
* only asks for tools carries `tool_calls` and no content at all (Go's
|
|
1057
|
+
* `omitzero`-style SDKs omit the key rather than send `null`/`""`). Keeping
|
|
1058
|
+
* an empty string content invites the upstream to fall back to its prose/DSML
|
|
1059
|
+
* channel; omitting the key keeps it on the structured `tool_calls` channel.
|
|
1060
|
+
*/
|
|
1061
|
+
export function omitBuddyAssistantToolCallContent(messages: Array<Record<string, unknown>>): void {
|
|
1062
|
+
for (const message of messages) {
|
|
1063
|
+
if (!isRecord(message) || message["role"] !== "assistant") continue;
|
|
1064
|
+
const calls = message["tool_calls"];
|
|
1065
|
+
if (!Array.isArray(calls) || calls.length === 0) continue;
|
|
1066
|
+
const content = message["content"];
|
|
1067
|
+
if (typeof content === "string" && content.trim().length === 0) {
|
|
1068
|
+
delete message["content"];
|
|
1069
|
+
} else if (Array.isArray(content) && content.length === 0) {
|
|
1070
|
+
delete message["content"];
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
/**
|
|
1076
|
+
* Forwards the caller's parallel tool-call intent onto the buddy wire.
|
|
1077
|
+
*
|
|
1078
|
+
* The canonical control is `generation_controls.parallel_tool_calls`; the
|
|
1079
|
+
* reference gateway always passes it through so upstream keeps the structured
|
|
1080
|
+
* tool channel, and the same is applied here.
|
|
1081
|
+
*/
|
|
1082
|
+
export function injectBuddyParallelToolCalls(
|
|
1083
|
+
payload: Record<string, unknown>,
|
|
1084
|
+
request: CanonicalRequest,
|
|
1085
|
+
): void {
|
|
1086
|
+
const parallel = request.generation_controls?.parallel_tool_calls;
|
|
1087
|
+
if (parallel !== undefined) payload["parallel_tool_calls"] = parallel;
|
|
1088
|
+
}
|
|
1089
|
+
|
|
1090
|
+
export interface BuddyMessageFinalizeOptions {
|
|
1091
|
+
/** The fixed leading system prompt installed on the wire. */
|
|
1092
|
+
readonly leadPrompt: string;
|
|
1093
|
+
/** Coalesce consecutive user turns (default true). */
|
|
1094
|
+
readonly coalesceConsecutiveUser?: boolean;
|
|
1095
|
+
/** Drop empty-content turns the upstream rejects (default true). */
|
|
1096
|
+
readonly dropEmpty?: boolean;
|
|
1097
|
+
/** Guarantee the wire opens with a system turn (default true). */
|
|
1098
|
+
readonly ensureLeadingSystem?: boolean;
|
|
1099
|
+
/** Omit content on assistant tool_calls-only turns (default true). */
|
|
1100
|
+
readonly omitToolCallContent?: boolean;
|
|
1101
|
+
/** Auto-inject ephemeral prompt-cache markers (default true). */
|
|
1102
|
+
readonly autoPromptCache?: boolean;
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
/**
|
|
1106
|
+
* The single authoritative buddy message-shape pipeline. Every variant runs
|
|
1107
|
+
* the same sequence — coalesce, drop empty turns, guarantee the leading system
|
|
1108
|
+
* turn, omit content on tool_calls-only assistant turns, then auto-cache — so
|
|
1109
|
+
* the wire shape cannot drift between CodeBuddy intl, CN, and WorkBuddy.
|
|
1110
|
+
*/
|
|
1111
|
+
export function finalizeBuddyMessages(
|
|
991
1112
|
messages: Array<Record<string, unknown>>,
|
|
992
|
-
|
|
1113
|
+
options: BuddyMessageFinalizeOptions,
|
|
993
1114
|
): void {
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
1115
|
+
if (options.coalesceConsecutiveUser !== false) {
|
|
1116
|
+
coalesceConsecutiveUserMessages(messages);
|
|
1117
|
+
}
|
|
1118
|
+
if (options.dropEmpty !== false) {
|
|
1119
|
+
dropEmptyBuddyMessages(messages);
|
|
1120
|
+
}
|
|
1121
|
+
if (options.ensureLeadingSystem !== false) {
|
|
1122
|
+
ensureBuddyLeadingSystem(messages, options.leadPrompt);
|
|
1123
|
+
}
|
|
1124
|
+
if (options.omitToolCallContent !== false) {
|
|
1125
|
+
omitBuddyAssistantToolCallContent(messages);
|
|
1126
|
+
}
|
|
1127
|
+
if (options.autoPromptCache !== false) {
|
|
1128
|
+
applyBuddyAutoPromptCache(messages);
|
|
1129
|
+
}
|
|
997
1130
|
}
|
|
998
1131
|
|
|
999
1132
|
/**
|
|
@@ -1035,5 +1168,5 @@ export function applyBuddySystemPrompt(
|
|
|
1035
1168
|
messages.push({ ...message });
|
|
1036
1169
|
}
|
|
1037
1170
|
}
|
|
1038
|
-
finalizeBuddyMessages(messages, systemPrompt);
|
|
1171
|
+
finalizeBuddyMessages(messages, { leadPrompt: systemPrompt });
|
|
1039
1172
|
}
|
|
@@ -24,6 +24,7 @@ import { providerBaseUrl } from "../../provider-metadata";
|
|
|
24
24
|
import { sleep } from "../../../shared/sleep";
|
|
25
25
|
import { WORKBUDDY_DOMAIN } from "./workbuddy/shared";
|
|
26
26
|
import { BUDDY_PROVIDER_IDS } from "../../provider-metadata";
|
|
27
|
+
import { QODER_CHECKIN_PROVIDER_IDS } from "../qoder/qoder-checkin";
|
|
27
28
|
import {
|
|
28
29
|
buildWorkBuddyUserAgent,
|
|
29
30
|
getWorkBuddyClientVersion,
|
|
@@ -38,7 +39,10 @@ import { codebuddyDomain, type CodeBuddyVariant } from "./codebuddy/shared";
|
|
|
38
39
|
* shared family set so this list cannot drift from the health recorder's or the
|
|
39
40
|
* request preparer's.
|
|
40
41
|
*/
|
|
41
|
-
export const DAILY_CHECKIN_PROVIDER_IDS: readonly string[] = [
|
|
42
|
+
export const DAILY_CHECKIN_PROVIDER_IDS: readonly string[] = [
|
|
43
|
+
...BUDDY_PROVIDER_IDS,
|
|
44
|
+
...QODER_CHECKIN_PROVIDER_IDS,
|
|
45
|
+
];
|
|
42
46
|
|
|
43
47
|
/** Abort budget for one upstream check-in request. */
|
|
44
48
|
const TIMEOUT_MS = 15_000;
|
|
@@ -337,35 +337,6 @@ export async function redeemBuddyStreakTier(
|
|
|
337
337
|
// 3. Streak lottery
|
|
338
338
|
// ---------------------------------------------------------------------------
|
|
339
339
|
|
|
340
|
-
const LOTTERY_CHANCES_PATH = "/activity/growth/lottery/chances";
|
|
341
|
-
const LOTTERY_DRAW_PATH = "/activity/growth/lottery/draw";
|
|
342
|
-
|
|
343
|
-
/** Remaining draw chances granted by a redeemed tier. */
|
|
344
|
-
export async function readBuddyLotteryChances(
|
|
345
|
-
options: BuddyGrowthCall,
|
|
346
|
-
): Promise<number | null> {
|
|
347
|
-
const reply = await call(options, "GET", LOTTERY_CHANCES_PATH);
|
|
348
|
-
if (!reply.ok || reply.data === null) return null;
|
|
349
|
-
const value = Number(reply.data["balance"]);
|
|
350
|
-
return Number.isFinite(value) ? value : null;
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
/**
|
|
354
|
-
* Draws once. A fresh `client_token` per draw: reusing one lets the upstream
|
|
355
|
-
* treat the draw as a duplicate and swallow it.
|
|
356
|
-
*/
|
|
357
|
-
export async function drawBuddyLottery(
|
|
358
|
-
options: BuddyGrowthCall,
|
|
359
|
-
): Promise<BuddyGrowthResult> {
|
|
360
|
-
const reply = await call(options, "POST", LOTTERY_DRAW_PATH, {
|
|
361
|
-
client_token: clientToken(options, "draw"),
|
|
362
|
-
});
|
|
363
|
-
if (refused(reply, 400, ["insufficient", "disabled", "no chance", "未开启"])) {
|
|
364
|
-
return { state: "not_eligible", error: null, credit: null };
|
|
365
|
-
}
|
|
366
|
-
return done(reply);
|
|
367
|
-
}
|
|
368
|
-
|
|
369
340
|
// ---------------------------------------------------------------------------
|
|
370
341
|
// 4. Cat travel
|
|
371
342
|
// ---------------------------------------------------------------------------
|
|
@@ -523,56 +494,3 @@ export async function adoptFirstBuddy(
|
|
|
523
494
|
return done(reply);
|
|
524
495
|
}
|
|
525
496
|
|
|
526
|
-
// ---------------------------------------------------------------------------
|
|
527
|
-
// 6. Trial gift pack + gift/compensation claims
|
|
528
|
-
// ---------------------------------------------------------------------------
|
|
529
|
-
|
|
530
|
-
const TRIAL_PATH = "/billing/ide/trial";
|
|
531
|
-
const GIFT_PATH = "/billing/meter/claim-gift";
|
|
532
|
-
const COMPENSATION_PATH = "/billing/meter/claim-compensation";
|
|
533
|
-
/** "Already claimed" on the trial pack. */
|
|
534
|
-
const TRIAL_ALREADY_CODE = 14051;
|
|
535
|
-
|
|
536
|
-
/**
|
|
537
|
-
* Claims the one-off trial credit pack.
|
|
538
|
-
*
|
|
539
|
-
* International only: the reference reports the CN hosts do not serve this
|
|
540
|
-
* endpoint at all, and a 404 there is a `skip`, not a failure.
|
|
541
|
-
*/
|
|
542
|
-
export async function claimBuddyTrial(
|
|
543
|
-
options: BuddyGrowthCall,
|
|
544
|
-
): Promise<BuddyGrowthResult> {
|
|
545
|
-
const reply = await call(options, "POST", TRIAL_PATH, {});
|
|
546
|
-
if (coded(reply, TRIAL_ALREADY_CODE) || refused(reply, 400, ["14051"])) {
|
|
547
|
-
return { state: "already", error: null, credit: null };
|
|
548
|
-
}
|
|
549
|
-
// Not served on this host at all — a 404/405 here is a routing fact, not an
|
|
550
|
-
// account problem.
|
|
551
|
-
if (reply.status === 404 || reply.status === 405) {
|
|
552
|
-
return { state: "skipped", error: null, credit: null };
|
|
553
|
-
}
|
|
554
|
-
return done(reply);
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
/**
|
|
558
|
-
* Claims the newcomer gift, if one is present.
|
|
559
|
-
*
|
|
560
|
-
* Once per account; a business error afterwards is the normal "already claimed"
|
|
561
|
-
* answer and is never a failure.
|
|
562
|
-
*/
|
|
563
|
-
export async function claimBuddyGift(
|
|
564
|
-
options: BuddyGrowthCall,
|
|
565
|
-
): Promise<BuddyGrowthResult> {
|
|
566
|
-
const reply = await call(options, "POST", GIFT_PATH, {});
|
|
567
|
-
if (!reply.ok) return { state: "already", error: null, credit: null };
|
|
568
|
-
return done(reply);
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
/** Claims an activity compensation, if one is present. Same once-only semantics. */
|
|
572
|
-
export async function claimBuddyCompensation(
|
|
573
|
-
options: BuddyGrowthCall,
|
|
574
|
-
): Promise<BuddyGrowthResult> {
|
|
575
|
-
const reply = await call(options, "POST", COMPENSATION_PATH, {});
|
|
576
|
-
if (!reply.ok) return { state: "already", error: null, credit: null };
|
|
577
|
-
return done(reply);
|
|
578
|
-
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ProviderDispatchContext } from "../../provider-contracts";
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
2
3
|
import { decodeJwtPayload } from "../../authentication/oauth-flow-store";
|
|
3
4
|
|
|
4
5
|
/**
|
|
@@ -96,3 +97,27 @@ export function buddyEnterprise(context?: ProviderDispatchContext): string | und
|
|
|
96
97
|
}
|
|
97
98
|
return undefined;
|
|
98
99
|
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Per-account device binding (`qimei36`) for the buddy providers.
|
|
103
|
+
*
|
|
104
|
+
* The desktop auth state sends `X-Device-Id-List: {"qimei36":"…"}` alongside
|
|
105
|
+
* the Turing token — a second, independent device identity. Pinning it per
|
|
106
|
+
* account makes every account in a pool present a distinct device to the
|
|
107
|
+
* upstream risk control instead of sharing the machine's single fingerprint.
|
|
108
|
+
*
|
|
109
|
+
* Resolution order:
|
|
110
|
+
* 1. `auth_state.qimei36` — explicit operator-set binding (imported or edited).
|
|
111
|
+
* 2. Determinstic derivation from the internal account id (sha256 → 32 hex).
|
|
112
|
+
* This keeps the binding stable across restarts without a database write,
|
|
113
|
+
* and gives each freshly-added account its own id automatically.
|
|
114
|
+
*
|
|
115
|
+
* Returns `undefined` only when no account id is available (unauthenticated).
|
|
116
|
+
*/
|
|
117
|
+
export function resolveBuddyDeviceBinding(context?: ProviderDispatchContext): string | undefined {
|
|
118
|
+
const explicit = context?.credential.auth_state?.["qimei36"];
|
|
119
|
+
if (typeof explicit === "string" && explicit.trim().length > 0) return explicit.trim();
|
|
120
|
+
const accountId = context?.credential.account_id;
|
|
121
|
+
if (accountId === undefined || accountId.length === 0) return undefined;
|
|
122
|
+
return createHash("sha256").update(`wb2a:qimei36:${accountId}`).digest("hex").slice(0, 32);
|
|
123
|
+
}
|