@bitkyc08/opencodex 2.84.0 → 2.87.0
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/README.md +0 -4
- package/bin/ocx.mjs +61 -3
- package/gui/dist/assets/App-ZfYbLHkB.js +58 -0
- package/gui/dist/assets/App-wT3mXqFz.css +1 -0
- package/gui/dist/assets/{Tray-C9TGtwad.js → Tray-DCd-A1az.js} +1 -1
- package/gui/dist/assets/index-CfYDMj34.js +94 -0
- package/gui/dist/assets/index-DPeB9HpV.css +1 -0
- package/gui/dist/assets/quota-summary-ChvmKGOk.js +1 -0
- package/gui/dist/index.html +2 -2
- package/package.json +1 -2
- package/src/adapters/anthropic/beta-allowlist.ts +91 -11
- package/src/adapters/anthropic/beta-body-pairs.ts +93 -0
- package/src/adapters/anthropic/client-identity.ts +1 -1
- package/src/adapters/anthropic/passthrough.ts +16 -3
- package/src/adapters/anthropic-progress.ts +84 -0
- package/src/adapters/anthropic.ts +108 -21
- package/src/adapters/azure.ts +19 -1
- package/src/adapters/base.ts +2 -0
- package/src/adapters/claude-cli/adapter.ts +111 -23
- package/src/adapters/claude-cli/profiles.ts +2 -1
- package/src/adapters/claude-cli/stable-replay.ts +108 -0
- package/src/adapters/claude-cli/usage-admission.ts +230 -0
- package/src/adapters/client-fingerprint.ts +24 -0
- package/src/adapters/coding-agent/protocol.ts +1 -1
- package/src/adapters/coding-agent/turn.ts +40 -11
- package/src/adapters/cursor/arg-codec.ts +17 -5
- package/src/adapters/cursor/catalog.ts +8 -0
- package/src/adapters/cursor/effort-map.ts +3 -0
- package/src/adapters/cursor/freeform-args.ts +75 -0
- package/src/adapters/cursor/protobuf-events.ts +36 -23
- package/src/adapters/dsh-account.ts +97 -0
- package/src/adapters/google-antigravity-wire.ts +22 -1
- package/src/adapters/google.ts +37 -10
- package/src/adapters/openai-chat/messages.ts +4 -3
- package/src/adapters/openai-chat/passthrough.ts +8 -2
- package/src/adapters/openai-chat/reasoning-replay.ts +52 -0
- package/src/adapters/openai-chat/response-events.ts +59 -1
- package/src/adapters/openai-chat.ts +8 -5
- package/src/adapters/openai-responses/compaction-search-history.ts +14 -7
- package/src/adapters/openai-responses/passthrough.ts +57 -125
- package/src/adapters/openai-responses/request-strips.ts +25 -3
- package/src/adapters/openai-responses/transport.ts +81 -0
- package/src/adapters/qoder/adapter.ts +44 -3
- package/src/adapters/registry.ts +5 -0
- package/src/adapters/zed.ts +26 -1
- package/src/advisor/activity.ts +55 -0
- package/src/advisor/budget.ts +62 -0
- package/src/advisor/consult.ts +223 -0
- package/src/advisor/context.ts +238 -0
- package/src/advisor/disclosure.ts +20 -0
- package/src/advisor/evidence-tool.ts +21 -0
- package/src/advisor/evidence.ts +117 -0
- package/src/advisor/runtime.ts +353 -0
- package/src/advisor/settings.ts +144 -0
- package/src/advisor/state.ts +369 -0
- package/src/advisor/synthetic-tool.ts +33 -0
- package/src/bridge/errors.ts +24 -1
- package/src/bridge/internal.ts +3 -1
- package/src/bridge/response-json.ts +18 -3
- package/src/bridge/sse.ts +58 -8
- package/src/bridge/terminal-refusal.ts +59 -0
- package/src/chat/inbound.ts +30 -6
- package/src/chat/outbound.ts +5 -0
- package/src/claude/desktop-3p-library.ts +72 -7
- package/src/claude/desktop-3p.ts +75 -19
- package/src/claude/desktop-first-party.ts +1 -1
- package/src/claude/desktop-picker-platform.ts +4 -0
- package/src/claude/desktop-picker-profile.ts +11 -4
- package/src/claude/desktop-picker.ts +15 -8
- package/src/claude/desktop-profile.ts +10 -1
- package/src/claude/desktop-remote-store-io.ts +5 -3
- package/src/claude/desktop-system-proxy.ts +50 -4
- package/src/claude/inbound-content-options.ts +11 -0
- package/src/claude/inbound.ts +154 -50
- package/src/claude/intercept/local-ca.ts +8 -1
- package/src/claude/intercept/picker-ca-startup.ts +2 -1
- package/src/claude/intercept/picker-runtime.ts +6 -5
- package/src/claude/intercept/picker-trust-windows.ts +338 -0
- package/src/claude/intercept/picker-trust.ts +19 -0
- package/src/claude/intercept/port-policy.ts +30 -0
- package/src/claude/intercept/runtime.ts +97 -94
- package/src/claude/model-default-settings.ts +154 -0
- package/src/claude/model-info.ts +7 -7
- package/src/claude/outbound.ts +51 -19
- package/src/cli/account-api.ts +1 -0
- package/src/cli/account-policy-dto.ts +6 -0
- package/src/cli/account-policy.ts +34 -5
- package/src/cli/account-preservation.ts +44 -0
- package/src/cli/account.ts +8 -2
- package/src/cli/advisor.ts +165 -0
- package/src/cli/auxiliary-listener-preflight.ts +37 -0
- package/src/cli/capabilities-accounts.ts +20 -1
- package/src/cli/capabilities-agents-routing.ts +23 -2
- package/src/cli/capabilities-base.ts +39 -11
- package/src/cli/capabilities-observe-system.ts +63 -3
- package/src/cli/capabilities-provider-models.ts +11 -0
- package/src/cli/chatgpt-command.ts +35 -14
- package/src/cli/claude.ts +46 -6
- package/src/cli/codex-cli-update.ts +151 -41
- package/src/cli/codex-provider.ts +44 -0
- package/src/cli/combo-input.ts +10 -1
- package/src/cli/combo-result.ts +1 -1
- package/src/cli/combo.ts +2 -1
- package/src/cli/cross-home-owner.ts +17 -14
- package/src/cli/dispatch.ts +30 -5
- package/src/cli/doctor.ts +15 -1
- package/src/cli/ensure-desktop-cold-start.ts +382 -0
- package/src/cli/export-command.ts +1 -1
- package/src/cli/help.ts +4 -2
- package/src/cli/index.ts +14 -5
- package/src/cli/integration-plan-dto.ts +3 -0
- package/src/cli/log-history.ts +172 -0
- package/src/cli/models-runtime-subcommands.ts +1 -0
- package/src/cli/models-runtime.ts +72 -8
- package/src/cli/observe-stream.ts +2 -0
- package/src/cli/observe.ts +16 -4
- package/src/cli/provider.ts +27 -5
- package/src/cli/registry.ts +41 -9
- package/src/cli/restart-scope.ts +3 -1
- package/src/cli/runtime-api.ts +5 -3
- package/src/cli/system-command.ts +40 -8
- package/src/cli/uninstall-client-state.ts +32 -0
- package/src/cli/usage-compare.ts +309 -0
- package/src/cli/usage-model-search.ts +4 -4
- package/src/cli/usage-report.ts +2 -2
- package/src/cli/v2.ts +1 -1
- package/src/client/machine-listener.ts +1 -3
- package/src/client/runtime.ts +1 -0
- package/src/clients/client-path.ts +51 -0
- package/src/clients/config-export/contracts.ts +3 -1
- package/src/clients/config-export/model-metadata.ts +3 -2
- package/src/clients/config-export/qoder.ts +93 -0
- package/src/clients/config-export.ts +35 -48
- package/src/clients/gentle-shell-path.ts +87 -0
- package/src/codex/account-auto-switch.ts +2 -0
- package/src/codex/account-identity.ts +29 -0
- package/src/codex/account-label.ts +1 -1
- package/src/codex/account-lifecycle.ts +21 -5
- package/src/codex/account-preservation.ts +28 -0
- package/src/codex/account-use-remaining.ts +23 -0
- package/src/codex/app-server-processes.ts +97 -7
- package/src/codex/app-server-restart-service.ts +55 -4
- package/src/codex/auth-api/account-list.ts +4 -0
- package/src/codex/auth-api/account-pause-group.ts +6 -50
- package/src/codex/auth-api/account-preservation.ts +57 -0
- package/src/codex/auth-api/pool-mode-gate.ts +5 -2
- package/src/codex/auth-api/routes.ts +8 -0
- package/src/codex/auth-api/use-remaining-route.ts +36 -0
- package/src/codex/auth-api.ts +0 -1
- package/src/codex/auth-context.ts +81 -7
- package/src/codex/autostart-health.ts +4 -0
- package/src/codex/catalog/auto-review.ts +20 -3
- package/src/codex/catalog/build-entries.ts +19 -2
- package/src/codex/catalog/combo-member.ts +14 -5
- package/src/codex/catalog/configured-tiers.ts +50 -0
- package/src/codex/catalog/effort.ts +4 -1
- package/src/codex/catalog/gather-capture.ts +2 -0
- package/src/codex/catalog/metadata.ts +11 -9
- package/src/codex/catalog/model-hints.ts +42 -19
- package/src/codex/catalog/model-visibility.ts +3 -1
- package/src/codex/catalog/native-ultrafast.ts +106 -0
- package/src/codex/catalog/parsing.ts +35 -86
- package/src/codex/catalog/provider-models.ts +40 -34
- package/src/codex/catalog/retained-sync.ts +2 -0
- package/src/codex/catalog/routed-gather.ts +33 -16
- package/src/codex/catalog/vendor-context.ts +82 -0
- package/src/codex/cli-install-provenance.ts +105 -17
- package/src/codex/cli-update-plan.ts +545 -0
- package/src/codex/config-write-lock.ts +63 -4
- package/src/codex/convergence.ts +2 -0
- package/src/codex/desktop-app/picker-freshness.ts +96 -0
- package/src/codex/desktop-app/types.ts +2 -0
- package/src/codex/desktop-app/windows.ts +29 -16
- package/src/codex/desktop-app-restart.ts +25 -2
- package/src/codex/desktop-compatibility/certificate-service.ts +99 -0
- package/src/codex/desktop-compatibility/certificate-store.ts +193 -0
- package/src/codex/desktop-compatibility/connection-store.ts +87 -0
- package/src/codex/desktop-compatibility/installed-build.ts +73 -0
- package/src/codex/desktop-compatibility/json-body.ts +29 -0
- package/src/codex/desktop-compatibility/native-identity.ts +62 -0
- package/src/codex/desktop-compatibility/relay-listener.ts +93 -0
- package/src/codex/desktop-compatibility/routing-binding.ts +10 -0
- package/src/codex/desktop-compatibility/routing-preflight.ts +69 -0
- package/src/codex/desktop-compatibility/runtime-ownership.ts +28 -0
- package/src/codex/desktop-compatibility/runtime.ts +182 -0
- package/src/codex/desktop-compatibility/service.ts +30 -0
- package/src/codex/desktop-compatibility/startup-settings.ts +50 -0
- package/src/codex/desktop-compatibility/usage-activation.ts +86 -0
- package/src/codex/desktop-compatibility/usage-controlled-fetch.ts +59 -0
- package/src/codex/desktop-compatibility/usage-controller.ts +107 -0
- package/src/codex/desktop-compatibility/usage-policy.ts +59 -0
- package/src/codex/desktop-compatibility/usage-refresh.ts +33 -0
- package/src/codex/desktop-compatibility/usage-sse-controller.ts +86 -0
- package/src/codex/desktop-compatibility/windows-activation-source.ts +44 -0
- package/src/codex/desktop-compatibility/windows-certificate-trust.ts +81 -0
- package/src/codex/desktop-compatibility/windows-key-protection.ts +165 -0
- package/src/codex/desktop-compatibility/windows-package-command.ts +86 -0
- package/src/codex/desktop-compatibility/windows-package-launch.ts +28 -0
- package/src/codex/features.ts +85 -45
- package/src/codex/history-provider.ts +1 -1
- package/src/codex/inject/config-write-section.ts +4 -1
- package/src/codex/inject/history-failure.ts +27 -0
- package/src/codex/inject/remove.ts +18 -6
- package/src/codex/inject/restore.ts +24 -2
- package/src/codex/inject/routing-classify.ts +4 -3
- package/src/codex/inject.ts +18 -27
- package/src/codex/journal.ts +16 -1
- package/src/codex/main-account-hard-lock.ts +30 -1
- package/src/codex/main-account.ts +99 -15
- package/src/codex/native-main-refresh-mode.ts +217 -0
- package/src/codex/native-profile-startup.ts +1 -1
- package/src/codex/ocx-compaction-history.ts +12 -10
- package/src/codex/paths.ts +86 -1
- package/src/codex/provider-routing.ts +174 -0
- package/src/codex/refresh.ts +2 -2
- package/src/codex/routing/health-store.ts +2 -2
- package/src/codex/routing/pool-recovery.ts +79 -0
- package/src/codex/routing/selection.ts +4 -3
- package/src/codex/routing/thread-affinity.ts +1 -1
- package/src/codex/shim-diagnostics.ts +6 -0
- package/src/codex/shim-overlay.ts +7 -2
- package/src/codex/signed-in-identity.ts +43 -0
- package/src/codex/subagent-model-fallback.ts +1 -1
- package/src/codex/sync.ts +6 -1
- package/src/codex/user-identity.ts +16 -5
- package/src/codex/windows-local-appdata.ts +84 -0
- package/src/combos/failover.ts +6 -1
- package/src/combos/index.ts +1 -0
- package/src/combos/resolve.ts +46 -10
- package/src/combos/types.ts +15 -0
- package/src/config/diagnostics.ts +35 -7
- package/src/config/live-reconcile.ts +51 -2
- package/src/config/load-degrade.ts +23 -1
- package/src/config/opencode-wire-upgrade.ts +54 -0
- package/src/config/opencode-zen-upgrade.ts +52 -0
- package/src/config/owner-registry.ts +22 -17
- package/src/config/paths.ts +10 -3
- package/src/config/provider-validation.ts +45 -2
- package/src/config/schema/config-schema.ts +39 -0
- package/src/config/schema/desktop-compatibility.ts +9 -0
- package/src/config/schema/leaf-validators.ts +39 -9
- package/src/config/schema/main-account-hard-lock-fallback.ts +26 -0
- package/src/config/schema/native-reasoning-retention.ts +26 -0
- package/src/config.ts +2 -0
- package/src/generated/compatibility-version.json +962 -350
- package/src/generated/model-metadata.ts +1 -1
- package/src/grok/sync.ts +1 -1
- package/src/images/loop.ts +46 -8
- package/src/integrations/catalog-refresh.ts +1 -1
- package/src/integrations/config-io.ts +20 -2
- package/src/integrations/journal.ts +15 -1
- package/src/integrations/mutation-plan.ts +2 -0
- package/src/integrations/registry.ts +15 -0
- package/src/integrations/store.ts +3 -0
- package/src/integrations/writer.ts +10 -1
- package/src/lib/advisor-activation.ts +16 -0
- package/src/lib/background-work.ts +65 -0
- package/src/lib/bounded-file-read.ts +58 -0
- package/src/lib/bun-path-runtime.d.mts +10 -1
- package/src/lib/bun-path-runtime.mjs +62 -3
- package/src/lib/codex-restart-contract.ts +15 -0
- package/src/lib/desktop-proxy-route.ts +30 -0
- package/src/lib/desktop-upstream-tunnel.ts +72 -0
- package/src/lib/empty-tool-catalog.ts +62 -0
- package/src/lib/errors.ts +26 -3
- package/src/lib/gcp-adc.ts +96 -9
- package/src/lib/gcp-credential-input.ts +12 -0
- package/src/lib/lab-live-route-production.ts +5 -0
- package/src/lib/local-internal-call-capability.ts +55 -0
- package/src/lib/opaque-proxy-tunnel.ts +217 -0
- package/src/lib/package-tree-integrity.ts +2 -2
- package/src/lib/redact.ts +2 -2
- package/src/lib/socks5-fetch.ts +48 -71
- package/src/lib/socks5-handshake.ts +112 -0
- package/src/lib/translator-budget.ts +7 -1
- package/src/oauth/account-quota-rank.ts +69 -2
- package/src/oauth/anthropic-account-refusal.ts +20 -2
- package/src/oauth/anthropic-oauth-definitions.ts +3 -1
- package/src/oauth/anthropic-routing.ts +1 -21
- package/src/oauth/anthropic.ts +38 -7
- package/src/oauth/callback-server.ts +27 -1
- package/src/oauth/chatgpt-device.ts +1 -1
- package/src/oauth/chatgpt-shared.ts +83 -0
- package/src/oauth/chatgpt.ts +8 -72
- package/src/oauth/dsh.ts +203 -0
- package/src/oauth/generic-account-failover.ts +62 -12
- package/src/oauth/generic-session-affinity.ts +125 -0
- package/src/oauth/index.ts +188 -111
- package/src/oauth/kiro-device-login.ts +2 -1
- package/src/oauth/kiro.ts +7 -1
- package/src/oauth/local-token-detect.ts +21 -1
- package/src/oauth/login-cli-errors.ts +44 -0
- package/src/oauth/login-cli.ts +69 -7
- package/src/oauth/login-flow-state.ts +371 -23
- package/src/oauth/meta-muse-errors.ts +41 -0
- package/src/oauth/meta-muse-keychain.ts +52 -0
- package/src/oauth/meta-muse.ts +51 -84
- package/src/oauth/model-discovery-auth.ts +3 -0
- package/src/oauth/pool-settings-capability.ts +4 -2
- package/src/oauth/refresh-flight-retirement.ts +48 -0
- package/src/oauth/session-affinity-key.ts +23 -0
- package/src/oauth/store-anthropic-instance.ts +9 -2
- package/src/oauth/store.ts +12 -5
- package/src/protocols/codecs/chat-ir.ts +467 -0
- package/src/protocols/codecs/chat.ts +10 -1
- package/src/protocols/codecs/messages-ir.ts +500 -0
- package/src/protocols/encoders/adapter-events.ts +5 -4
- package/src/protocols/encoders/messages.ts +9 -6
- package/src/protocols/features.ts +10 -2
- package/src/protocols/path.ts +11 -1
- package/src/protocols/plan-snapshot.ts +39 -16
- package/src/protocols/plan.ts +9 -1
- package/src/protocols/provider-summary.ts +2 -1
- package/src/protocols/settings.ts +8 -0
- package/src/providers/antigravity-account-roster.ts +102 -0
- package/src/providers/antigravity-models.ts +85 -7
- package/src/providers/api-key-cooldowns.ts +49 -0
- package/src/providers/api-key-resolve.ts +11 -6
- package/src/providers/api-key-response-ownership.ts +57 -0
- package/src/providers/api-key-selection.ts +31 -6
- package/src/providers/api-keys.ts +214 -3
- package/src/providers/claude-cli-usage.ts +96 -0
- package/src/providers/dsh-account-destination.ts +31 -0
- package/src/providers/dsh-account-models.ts +41 -0
- package/src/providers/gcp-credential-cleanup.ts +59 -0
- package/src/providers/key-failover.ts +54 -50
- package/src/providers/key-store.ts +7 -2
- package/src/providers/model-rename-fields.ts +5 -0
- package/src/providers/model-rename-migration.ts +7 -5
- package/src/providers/openai-tiers-destination.ts +11 -2
- package/src/providers/openai-tiers.ts +5 -12
- package/src/providers/opencode-go-transport.ts +3 -1
- package/src/providers/quota/account-cache.ts +6 -5
- package/src/providers/quota/account-row.ts +34 -0
- package/src/providers/quota/antigravity.ts +28 -7
- package/src/providers/quota/dsh-account.ts +70 -0
- package/src/providers/quota/vendor-probes-oauth.ts +1 -93
- package/src/providers/quota-types.ts +2 -0
- package/src/providers/quota.ts +14 -20
- package/src/providers/registry/entries-core.ts +44 -14
- package/src/providers/registry/entries-extended.ts +19 -0
- package/src/providers/registry/types.ts +1 -1
- package/src/providers/request-pacing.ts +77 -19
- package/src/providers/routed-config.ts +339 -0
- package/src/providers/service-tier.ts +2 -1
- package/src/quota/window-mapping.ts +5 -2
- package/src/responses/anthropic-progress.ts +34 -0
- package/src/responses/assistant-replay.ts +35 -0
- package/src/responses/compaction-history-framing.ts +65 -0
- package/src/responses/compaction.ts +6 -1
- package/src/responses/incomplete-reason.ts +31 -0
- package/src/responses/parser.ts +78 -22
- package/src/responses/reasoning-envelope.ts +4 -0
- package/src/responses/reasoning-replay-cache.ts +24 -2
- package/src/responses/reasoning-retention.ts +173 -0
- package/src/responses/retained-compaction.ts +46 -0
- package/src/responses/schema.ts +23 -0
- package/src/responses/state.ts +32 -29
- package/src/router.ts +8 -321
- package/src/routing/health.ts +37 -22
- package/src/routing/history/build-file.ts +92 -0
- package/src/routing/history/indexer.ts +254 -221
- package/src/routing/history/ingest.ts +242 -0
- package/src/routing/history/rebuild-job.ts +111 -0
- package/src/routing/history/rebuild-protocol.ts +15 -0
- package/src/routing/history/rebuild-worker.ts +34 -0
- package/src/routing/profile.ts +1 -1
- package/src/server/admission-model-scope.ts +21 -1
- package/src/server/antigravity-direct-eligibility.ts +208 -0
- package/src/server/auth-cors.ts +15 -0
- package/src/server/chat-completions.ts +82 -10
- package/src/server/chat-native-reasoning.ts +123 -0
- package/src/server/chat-native-sse.ts +110 -7
- package/src/server/chat-native.ts +76 -17
- package/src/server/claude-messages.ts +164 -433
- package/src/server/effort-policy.ts +1 -1
- package/src/server/images.ts +15 -2
- package/src/server/index/desktop-compatibility-startup.ts +56 -0
- package/src/server/index/model-alias-id.ts +36 -0
- package/src/server/index/optional-listeners.ts +2 -0
- package/src/server/index/serve-options.ts +11 -3
- package/src/server/index/startup-warnings.ts +11 -0
- package/src/server/index/websocket-handler.ts +9 -0
- package/src/server/index.ts +14 -11
- package/src/server/inference/advisor-diagnostics.ts +31 -0
- package/src/server/inference/client-encoder-delivery.ts +6 -5
- package/src/server/login-shell-path.ts +146 -0
- package/src/server/management/advisor-routes.ts +202 -0
- package/src/server/management/agent-settings-routes.ts +22 -4
- package/src/server/management/claude-desktop-picker-routes.ts +1 -1
- package/src/server/management/combo-routes.ts +176 -50
- package/src/server/management/companion-routes.ts +4 -0
- package/src/server/management/config-routes.ts +77 -6
- package/src/server/management/context.ts +4 -0
- package/src/server/management/cursor-integration-routes.ts +1 -1
- package/src/server/management/desktop-compatibility-routes.ts +46 -0
- package/src/server/management/desktop-compatibility-runtime-routes.ts +46 -0
- package/src/server/management/desktop-compatibility-settings-routes.ts +27 -0
- package/src/server/management/model-routes.ts +13 -8
- package/src/server/management/model-rows.ts +14 -0
- package/src/server/management/native-integration-routes.ts +2 -7
- package/src/server/management/native-reasoning-retention-routes.ts +41 -0
- package/src/server/management/oauth-account-routes.ts +87 -14
- package/src/server/management/protocol-settings-patch.ts +1 -0
- package/src/server/management/provider-gcp-credential-routes.ts +29 -0
- package/src/server/management/provider-key-pacing-routes.ts +31 -0
- package/src/server/management/provider-overwrite-carry.ts +7 -0
- package/src/server/management/provider-routes.ts +25 -30
- package/src/server/management/route-registry.ts +21 -1
- package/src/server/management/sibling-guard.ts +1 -0
- package/src/server/management-api.ts +9 -1
- package/src/server/messages-antigravity-direct.ts +195 -0
- package/src/server/messages-native-tool-guard.ts +185 -0
- package/src/server/messages-native.ts +106 -22
- package/src/server/messages-shared.ts +431 -0
- package/src/server/relay.ts +1 -1
- package/src/server/request-log-terminal-status.ts +1 -1
- package/src/server/request-log.ts +13 -1
- package/src/server/responses/account-change-state.ts +8 -2
- package/src/server/responses/adapter-continuation.ts +48 -19
- package/src/server/responses/adapter-delivery.ts +43 -8
- package/src/server/responses/adapter-dispatch.ts +48 -4
- package/src/server/responses/advisor-plan-slot.ts +27 -0
- package/src/server/responses/advisor-retention.ts +117 -0
- package/src/server/responses/advisor-slot.ts +368 -0
- package/src/server/responses/code-mode-collab-surface.ts +116 -8
- package/src/server/responses/codex-ws-wire.ts +15 -8
- package/src/server/responses/compact.ts +303 -125
- package/src/server/responses/compaction-recovery.ts +18 -10
- package/src/server/responses/compaction-retention.ts +61 -0
- package/src/server/responses/context-overflow.ts +11 -6
- package/src/server/responses/core-auth.ts +6 -0
- package/src/server/responses/core-codex-account.ts +11 -1
- package/src/server/responses/core-combo-failure.ts +12 -1
- package/src/server/responses/core-combo.ts +141 -36
- package/src/server/responses/core-errors.ts +3 -3
- package/src/server/responses/core-lifetime.ts +7 -2
- package/src/server/responses/core-normalize.ts +11 -0
- package/src/server/responses/core-opaque-recovery.ts +35 -0
- package/src/server/responses/core-options.ts +51 -0
- package/src/server/responses/core-pipeline.ts +141 -0
- package/src/server/responses/core-replay.ts +25 -6
- package/src/server/responses/core.ts +3 -116
- package/src/server/responses/fetch-helpers.ts +73 -19
- package/src/server/responses/main-hard-lock-routing.ts +109 -0
- package/src/server/responses/native-response-control.ts +4 -2
- package/src/server/responses/passthrough-delivery.ts +19 -11
- package/src/server/responses/passthrough-dispatch.ts +29 -16
- package/src/server/responses/request-prepare.ts +144 -17
- package/src/server/responses/request-sidecar-auth.ts +2 -2
- package/src/server/responses/request-transport.ts +120 -27
- package/src/server/responses/response-effects.ts +1 -0
- package/src/server/responses/run-turn-execution.ts +166 -38
- package/src/server/responses/sidecar-execution.ts +98 -5
- package/src/server/responses/terminal-guard.ts +4 -17
- package/src/server/responses-undeclared-tool-guard.ts +30 -9
- package/src/server/startup-action-control.ts +16 -2
- package/src/server/startup-health-cache.ts +1 -0
- package/src/service/auth-preflight.ts +71 -0
- package/src/service/cli.ts +4 -2
- package/src/service/definition.ts +97 -0
- package/src/service/desktop-startup.ts +14 -0
- package/src/service/guards.ts +1 -61
- package/src/service/health-sentinel-linux.ts +112 -0
- package/src/service/health-sentinel-main.ts +99 -61
- package/src/service/health-sentinel-probe.ts +24 -7
- package/src/service/health.ts +5 -89
- package/src/service/launchd.ts +27 -7
- package/src/service/logging.ts +135 -0
- package/src/service/repair.ts +2 -1
- package/src/service/state.ts +7 -8
- package/src/service/systemd.ts +1 -1
- package/src/service/windows-guarded-manager.ts +2 -1
- package/src/service/windows-ops.ts +1 -1
- package/src/service/windows-scheduler.ts +2 -7
- package/src/service/windows-taskxml.ts +2 -2
- package/src/service/windows-wscript.ts +14 -0
- package/src/service.ts +4 -2
- package/src/sidecar/auth.ts +2 -0
- package/src/tray/windows-tray.ps1 +43 -24
- package/src/types/config.ts +124 -6
- package/src/types/opencode-go.ts +61 -0
- package/src/types/opencode-zen.ts +29 -0
- package/src/types/provider.ts +39 -6
- package/src/types/request.ts +38 -2
- package/src/types/tools.ts +2 -0
- package/src/types/wire.ts +29 -37
- package/src/types.ts +1 -0
- package/src/update/async-check.ts +1 -1
- package/src/update/badge.ts +2 -2
- package/src/update/index.ts +88 -15
- package/src/update/job-supervised-restart.ts +107 -0
- package/src/update/job.ts +32 -32
- package/src/update/notify.ts +1 -1
- package/src/update/npm-invocation.mjs +12 -2
- package/src/update/refresh-scheduler.ts +2 -2
- package/src/update/supervised-recovery.d.mts +27 -0
- package/src/update/supervised-recovery.mjs +157 -0
- package/src/usage/cache-diagnostic.ts +2 -0
- package/src/usage/cost.ts +48 -12
- package/src/usage/expected-prices.ts +15 -0
- package/src/usage/ledger-retention.ts +2 -1
- package/src/usage/ledger-scanner.ts +6 -5
- package/src/usage/log.ts +17 -8
- package/src/usage/summary.ts +22 -9
- package/src/usage/user-cost-overlays.ts +14 -19
- package/src/usage/user-cost-validation.ts +38 -0
- package/src/vision/eligibility.ts +13 -0
- package/src/web-search/gemini-executor.ts +2 -2
- package/src/web-search/loop.ts +55 -5
- package/src/web-search/progress-stream.ts +4 -1
- package/src/web-search/run-turn-loop.ts +9 -1
- package/assets/sponsors/packycode.png +0 -0
- package/gui/dist/assets/App-CSJvmjNs.css +0 -1
- package/gui/dist/assets/App-CZtqiGcw.js +0 -56
- package/gui/dist/assets/index-BB6uK6tU.css +0 -1
- package/gui/dist/assets/index-CTm4l-HQ.js +0 -94
- package/gui/dist/assets/quota-summary-BbEeRfPY.js +0 -1
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import { readExistingSpendSalt, spendPoolAliasesError, validatePoolAliasOwners } from "../lib/spend-pool-alias-validation";
|
|
2
2
|
import { isSubagentModelEntry, rawSubagentModelForce } from "./subagent-models";
|
|
3
|
+
import { validRemainingQuotaAccountIds } from "../codex/account-use-remaining";
|
|
3
4
|
import { protocolConfigSchema } from "./schema/config-schema";
|
|
4
5
|
import { createHash } from "node:crypto";
|
|
5
6
|
import { lstatSync, readFileSync } from "node:fs";
|
|
6
7
|
import { join } from "node:path";
|
|
7
8
|
import * as z from "zod/v4";
|
|
8
9
|
import { compactionRecoveryConfigError } from "./schema/compaction-recovery";
|
|
10
|
+
import { nativeReasoningRetentionConfigError } from "./schema/native-reasoning-retention";
|
|
11
|
+
import { desktopCompatibilityConfigError } from "./schema/desktop-compatibility";
|
|
9
12
|
import { blockedModelRedirectsError } from "./schema/blocked-model-redirects";
|
|
13
|
+
import { mainAccountHardLockFallbackError } from "./schema/main-account-hard-lock-fallback";
|
|
10
14
|
import type { OcxConfig } from "../types";
|
|
11
15
|
import { anthropicAccountPoolConfigError, anthropicOAuthInstanceConfigError, anthropicSidecarConfigError } from "./schema/anthropic-account-pool";
|
|
12
16
|
import { configReasoningPinsConfigError } from "./provider-validation";
|
|
@@ -17,6 +21,7 @@ import { isMissingPathError } from "./atomic-write";
|
|
|
17
21
|
import { getConfigDir, getConfigPath } from "./paths";
|
|
18
22
|
import { getDefaultConfig } from "./proxy-env";
|
|
19
23
|
import { salvageConfigCandidate } from "./salvage";
|
|
24
|
+
import { sanitizeOpenCodeOverridesForLoad } from "./opencode-wire-upgrade";
|
|
20
25
|
import {
|
|
21
26
|
sanitizeReasoningPinsForLoad,
|
|
22
27
|
sanitizeRetryOn429ForLoad,
|
|
@@ -24,6 +29,7 @@ import {
|
|
|
24
29
|
sanitizeModelCostsForLoad,
|
|
25
30
|
sanitizeAutoReviewForLoad,
|
|
26
31
|
degradedListenerWarnings,
|
|
32
|
+
degradedReasoningRetentionWarning,
|
|
27
33
|
degradedCodexAccountPriorityWarnings,
|
|
28
34
|
degradedCodexQuotaAutoRefreshWarning,
|
|
29
35
|
normalizeApiKeyIds,
|
|
@@ -70,6 +76,7 @@ import {
|
|
|
70
76
|
chatgptDesktopSchema,
|
|
71
77
|
chatgptDesktopConfigIssue,
|
|
72
78
|
compactionRoutingSchema,
|
|
79
|
+
reasoningRetentionSchema,
|
|
73
80
|
skillsConfigSchema,
|
|
74
81
|
memoryModelsSchema,
|
|
75
82
|
} from "./schema/leaf-validators";
|
|
@@ -135,6 +142,8 @@ function validFileConfigDiagnostics(config: OcxConfig, rawParsed: unknown): Conf
|
|
|
135
142
|
warnings.push(...degradedCodexAccountPriorityWarnings(rawParsed, normalized));
|
|
136
143
|
warnings.push(...degradedListenerWarnings(rawParsed, normalized));
|
|
137
144
|
if (blockedModelRedirectsError(rawParsed)) warnings.push("blockedModelRedirects ignored: expected a map of nonempty model keys to nonempty string targets");
|
|
145
|
+
if (nativeReasoningRetentionConfigError(rawParsed)) warnings.push("nativeReasoningRetention ignored: expected only optional boolean modelSwitch and accountSwitch fields; both remain off");
|
|
146
|
+
if (mainAccountHardLockFallbackError(rawParsed)) warnings.push("codexMainAccountHardLockFallback ignored: expected a nonblank model selector and an optional declared reasoningEffort");
|
|
138
147
|
const quotaAutoRefreshWarning = degradedCodexQuotaAutoRefreshWarning(rawParsed, normalized);
|
|
139
148
|
if (quotaAutoRefreshWarning) warnings.push(quotaAutoRefreshWarning);
|
|
140
149
|
if (rawEffort !== undefined && !isClaudeSubagentEffort(rawEffort)) {
|
|
@@ -163,6 +172,8 @@ function validFileConfigDiagnostics(config: OcxConfig, rawParsed: unknown): Conf
|
|
|
163
172
|
if (codexPoolWarning) warnings.push(codexPoolWarning);
|
|
164
173
|
const spendWarning = malformedSpendWarning(rawParsed);
|
|
165
174
|
if (spendWarning) warnings.push(spendWarning);
|
|
175
|
+
const retentionWarning = degradedReasoningRetentionWarning(rawParsed, normalized);
|
|
176
|
+
if (retentionWarning) warnings.push(retentionWarning);
|
|
166
177
|
if (rawPoolAliasesError(rawParsed)) {
|
|
167
178
|
warnings.push("spendPoolAliases invalid: configured pool admission is refused until the mapping is corrected");
|
|
168
179
|
}
|
|
@@ -403,12 +414,15 @@ function codexAccountPrioritiesError(value: unknown): string | null {
|
|
|
403
414
|
return null;
|
|
404
415
|
}
|
|
405
416
|
|
|
406
|
-
function
|
|
417
|
+
function codexAccountSelectionPreferencesError(value: unknown): string | null {
|
|
407
418
|
const raw = rawConfigRecord(value);
|
|
408
|
-
if (!raw
|
|
409
|
-
const
|
|
410
|
-
|
|
411
|
-
|
|
419
|
+
if (!raw) return null;
|
|
420
|
+
for (const key of ["codexAccountPriorityFailback", "codexPreserveSignedInAccount"]) {
|
|
421
|
+
if (raw[key] !== undefined && typeof raw[key] !== "boolean") {
|
|
422
|
+
return `schema_invalid: ${key}: must be a boolean or omitted`;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
return null;
|
|
412
426
|
}
|
|
413
427
|
|
|
414
428
|
/**
|
|
@@ -504,6 +518,12 @@ function creditCodexAccountIdsError(value: unknown): string | null {
|
|
|
504
518
|
return "schema_invalid: creditCodexAccountIds: must be an array of account ids or omitted";
|
|
505
519
|
}
|
|
506
520
|
|
|
521
|
+
function remainingQuotaAccountIdsError(value: unknown): string | null {
|
|
522
|
+
const ids = rawConfigRecord(value)?.codexUseRemainingQuotaAccountIds;
|
|
523
|
+
return ids === undefined || validRemainingQuotaAccountIds(ids) ? null
|
|
524
|
+
: "schema_invalid: codexUseRemainingQuotaAccountIds: must be an array of Codex account ids or omitted";
|
|
525
|
+
}
|
|
526
|
+
|
|
507
527
|
function oauthOpenBrowserError(value: unknown): string | null {
|
|
508
528
|
const raw = rawConfigRecord(value);
|
|
509
529
|
if (!raw || !Object.hasOwn(raw, "oauthOpenBrowser")) return null;
|
|
@@ -680,6 +700,10 @@ export function validateConfigCandidate(value: unknown): { ok: true; config: Ocx
|
|
|
680
700
|
return { ok: false, error: "schema_invalid: chatgptDesktop: requires an optional boolean appServerShim and no other fields" };
|
|
681
701
|
}
|
|
682
702
|
const compactionRouting = rawConfigRecord(value)?.compactionRouting;
|
|
703
|
+
const reasoningRetention = rawConfigRecord(value)?.reasoningRetention;
|
|
704
|
+
if (reasoningRetention !== undefined && !reasoningRetentionSchema.safeParse(reasoningRetention).success) {
|
|
705
|
+
return { ok: false, error: "schema_invalid: reasoningRetention: requires optional boolean enabled, maxContextPercent greater than 0 and at most 100 and/or positive integer maxTokens, with no other fields" };
|
|
706
|
+
}
|
|
683
707
|
if (compactionRouting !== undefined && !compactionRoutingSchema.safeParse(compactionRouting).success) {
|
|
684
708
|
return { ok: false, error: "schema_invalid: compactionRouting: requires a nonblank model, an optional valid reasoningEffort, and optional non-repeating triggers drawn from \"manual\" and \"auto\"" };
|
|
685
709
|
}
|
|
@@ -694,7 +718,9 @@ export function validateConfigCandidate(value: unknown): { ok: true; config: Ocx
|
|
|
694
718
|
const anthropicError = anthropicOAuthInstanceConfigError(value) ?? anthropicAccountPoolConfigError(value) ?? anthropicSidecarConfigError(value);
|
|
695
719
|
if (anthropicError) return { ok: false, error: anthropicError };
|
|
696
720
|
const boundaryError = blockedModelRedirectsError(value)
|
|
697
|
-
??
|
|
721
|
+
?? nativeReasoningRetentionConfigError(value)
|
|
722
|
+
?? mainAccountHardLockFallbackError(value)
|
|
723
|
+
?? compactionRecoveryConfigError(value) ?? desktopCompatibilityConfigError(value) ?? configReasoningPinsConfigError(value)
|
|
698
724
|
?? blankHostnameError(value)
|
|
699
725
|
?? (rawSubagentModelForce(value) !== undefined && !isSubagentModelEntry(rawSubagentModelForce(value)) ? "schema_invalid: claudeCode.subagentModelForce: expected a safe roster-style model id" : null)
|
|
700
726
|
?? claudeSubagentEffortError(value)
|
|
@@ -709,7 +735,7 @@ export function validateConfigCandidate(value: unknown): { ok: true; config: Ocx
|
|
|
709
735
|
?? codexPoolError(value)
|
|
710
736
|
?? googleAntigravityStaticCatalogVersionError(value)
|
|
711
737
|
?? codexAccountPrioritiesError(value)
|
|
712
|
-
??
|
|
738
|
+
?? codexAccountSelectionPreferencesError(value)
|
|
713
739
|
?? poolCredentialGroupsError(value)
|
|
714
740
|
?? codexQuotaAutoRefreshError(value)
|
|
715
741
|
?? codexAccountPickerEnabledError(value)
|
|
@@ -718,6 +744,7 @@ export function validateConfigCandidate(value: unknown): { ok: true; config: Ocx
|
|
|
718
744
|
?? oauthOpenBrowserError(value)
|
|
719
745
|
?? showCodexCreditsError(value)
|
|
720
746
|
?? creditCodexAccountIdsError(value)
|
|
747
|
+
?? remainingQuotaAccountIdsError(value)
|
|
721
748
|
?? runtimeRoleError(value)
|
|
722
749
|
?? remoteGuiConfigError(value)
|
|
723
750
|
?? clientConnectionConfigError(value)
|
|
@@ -739,6 +766,7 @@ export function validateConfigCandidate(value: unknown): { ok: true; config: Ocx
|
|
|
739
766
|
export function configDiagnosticsFromRaw(raw: string): ConfigDiagnostics {
|
|
740
767
|
try {
|
|
741
768
|
const parsed = JSON.parse(raw.replace(/^\uFEFF/, ""));
|
|
769
|
+
sanitizeOpenCodeOverridesForLoad(parsed);
|
|
742
770
|
sanitizeReasoningPinsForLoad(parsed);
|
|
743
771
|
// Same degradation as loadConfig: a hand-edited invalid retryOn429 must not trip the
|
|
744
772
|
// schema and send the caller a default-config fallback (the config command could then
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { OcxConfig, OcxProviderConfig } from "../types";
|
|
1
|
+
import type { OcxConfig, OcxProviderConfig, ProviderCostOverlay } from "../types";
|
|
2
2
|
import { configReasoningPinsConfigError } from "./provider-validation";
|
|
3
3
|
import { adoptCustomModelCatalogMigration, projectCustomModelCatalogMigration } from "../codex/custom-model-catalog-migration";
|
|
4
4
|
import { refreshPreservedProviderOwner } from "../usage/user-cost-overlays";
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
} from "./rebase-provenance";
|
|
17
17
|
import { withConfigMutationLockSync, bumpGenerationForCooperatingConfigWrite } from "./mutation-lock";
|
|
18
18
|
import { ConfigWritePublishedError, persistConfigUnlocked, readRawConfigJson } from "./persist-unlocked";
|
|
19
|
-
import { configDiagnosticsFromRaw, readConfigDiagnostics } from "./diagnostics";
|
|
19
|
+
import { configDiagnosticsFromRaw, readConfigDiagnostics, validateConfigCandidate } from "./diagnostics";
|
|
20
20
|
import { normalizePersistedClaudeCode } from "./load-degrade";
|
|
21
21
|
import { reconcileApiKeys } from "./api-key-reconcile";
|
|
22
22
|
import { captureLiveTopologyBaseline, LIVE_TOPOLOGY_KEYS, projectLiveTopology } from "./live-topology";
|
|
@@ -61,6 +61,35 @@ export function adoptPersistedCodexAccountSelection(
|
|
|
61
61
|
if (baseline) clearConfigTopLevelDeletionIntent(baseline, keys);
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
+
type ModelCostReplacement = { provider: string; modelId: string; cost: ProviderCostOverlay | null };
|
|
65
|
+
const modelCostReplacements = new WeakMap<OcxConfig, ModelCostReplacement>();
|
|
66
|
+
|
|
67
|
+
/** Explicit pricing PUT owns one whole row, even when its base tuple equals the baseline. */
|
|
68
|
+
export function withModelCostRowReplacement(
|
|
69
|
+
config: OcxConfig, provider: string, modelId: string, cost: ProviderCostOverlay | null, save: () => void,
|
|
70
|
+
): void {
|
|
71
|
+
const previous = modelCostReplacements.get(config);
|
|
72
|
+
const replacementCost = structuredClone(cost);
|
|
73
|
+
if (replacementCost?.promptLengthPricing?.mode === "automatic") delete replacementCost.promptLengthPricing;
|
|
74
|
+
modelCostReplacements.set(config, { provider, modelId, cost: replacementCost });
|
|
75
|
+
try { save(); }
|
|
76
|
+
finally {
|
|
77
|
+
if (previous) modelCostReplacements.set(config, previous);
|
|
78
|
+
else modelCostReplacements.delete(config);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function applyModelCostRowReplacement(config: OcxConfig): void {
|
|
83
|
+
const replacement = modelCostReplacements.get(config);
|
|
84
|
+
if (!replacement) return;
|
|
85
|
+
if (!Object.hasOwn(config.providers, replacement.provider)) throw new Error("pricing provider was removed before persistence");
|
|
86
|
+
const provider = config.providers[replacement.provider]!;
|
|
87
|
+
const costs = Object.assign(Object.create(null) as Record<string, ProviderCostOverlay>, provider.modelCosts ?? {});
|
|
88
|
+
if (replacement.cost === null) delete costs[replacement.modelId];
|
|
89
|
+
else costs[replacement.modelId] = structuredClone(replacement.cost);
|
|
90
|
+
provider.modelCosts = costs;
|
|
91
|
+
}
|
|
92
|
+
|
|
64
93
|
/**
|
|
65
94
|
* Adopt a committed discovery decision and its merge baseline as one synchronous step.
|
|
66
95
|
* Visibility writers assign and save synchronously; any future writer yielding between
|
|
@@ -138,6 +167,19 @@ export function adoptPersistedProviderIntoLiveConfig(
|
|
|
138
167
|
if (persistedConfig) refreshPreservedProviderOwner(config, persistedConfig);
|
|
139
168
|
}
|
|
140
169
|
|
|
170
|
+
/** Adopt only the committed next-start preference; preserve unrelated pending live edits. */
|
|
171
|
+
export function adoptPersistedDesktopCompatibility(config: OcxConfig, persisted: OcxConfig["desktopCompatibility"]): void {
|
|
172
|
+
const baseline = liveConfigBaseline.get(config);
|
|
173
|
+
const merged = reconcileConfigValue(baseline?.desktopCompatibility ?? MISSING_CONFIG_VALUE,
|
|
174
|
+
config.desktopCompatibility ?? MISSING_CONFIG_VALUE, persisted ?? MISSING_CONFIG_VALUE);
|
|
175
|
+
if (merged === MISSING_CONFIG_VALUE) delete config.desktopCompatibility;
|
|
176
|
+
else config.desktopCompatibility = merged as OcxConfig["desktopCompatibility"];
|
|
177
|
+
if (baseline) {
|
|
178
|
+
if (persisted === undefined) delete baseline.desktopCompatibility;
|
|
179
|
+
else baseline.desktopCompatibility = structuredClone(persisted);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
141
183
|
/** Test seam only: is this instance armed? */
|
|
142
184
|
export function claudeCodeBaselineArmed(config: OcxConfig): boolean {
|
|
143
185
|
return claudeCodeBaseline.has(config);
|
|
@@ -470,6 +512,10 @@ export function saveConfigPreservingClaudeCode(config: OcxConfig): void {
|
|
|
470
512
|
if (pinError) throw new Error(pinError);
|
|
471
513
|
let published = false;
|
|
472
514
|
const persist = (candidate: OcxConfig): void => {
|
|
515
|
+
if (modelCostReplacements.has(config)) {
|
|
516
|
+
const validation = validateConfigCandidate(candidate);
|
|
517
|
+
if (!validation.ok) throw new Error(validation.error);
|
|
518
|
+
}
|
|
473
519
|
const changed = persistConfigUnlocked(candidate);
|
|
474
520
|
published = true;
|
|
475
521
|
if (changed) bumpGenerationForCooperatingConfigWrite();
|
|
@@ -531,6 +577,9 @@ export function saveConfigPreservingClaudeCode(config: OcxConfig): void {
|
|
|
531
577
|
}
|
|
532
578
|
}
|
|
533
579
|
applyConfigObjectChildDeletions(config, childDeletions);
|
|
580
|
+
// Recursive three-way merging preserves sibling rows; explicit PUT/reset
|
|
581
|
+
// replaces only its model after merging, including absent policy fields.
|
|
582
|
+
applyModelCostRowReplacement(config);
|
|
534
583
|
if (claudeCodeBaseline.has(config)) {
|
|
535
584
|
if (onDisk !== undefined) {
|
|
536
585
|
const baseline = claudeCodeBaseline.get(config);
|
|
@@ -3,11 +3,15 @@ import { isSubagentModelEntry, rawSubagentModelForce } from "./subagent-models";
|
|
|
3
3
|
import { chmodSync, existsSync } from "node:fs";
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
import { compactionRecoveryConfigError } from "./schema/compaction-recovery";
|
|
6
|
+
import { nativeReasoningRetentionConfigError } from "./schema/native-reasoning-retention";
|
|
7
|
+
import { desktopCompatibilityConfigError } from "./schema/desktop-compatibility";
|
|
6
8
|
import { blockedModelRedirectsError } from "./schema/blocked-model-redirects";
|
|
9
|
+
import { mainAccountHardLockFallbackError } from "./schema/main-account-hard-lock-fallback";
|
|
7
10
|
import { anthropicSidecarConfigError } from "./schema/anthropic-account-pool";
|
|
8
11
|
import {
|
|
9
12
|
modelPinnedEffortsConfigError,
|
|
10
13
|
pinnedReasoningEffortConfigError,
|
|
14
|
+
autoReviewOverrideConfigError,
|
|
11
15
|
autoReviewModelOverridesConfigError,
|
|
12
16
|
autoReviewModelTargetConfigError,
|
|
13
17
|
modelCapabilitiesConfigError,
|
|
@@ -51,9 +55,12 @@ import { hasWarnedInheritedFastWireConflict, markWarnedInheritedFastWireConflict
|
|
|
51
55
|
|
|
52
56
|
export function hardenExistingSecret(path: string): void {
|
|
53
57
|
if (existsSync(path) && !isPassiveRead()) {
|
|
54
|
-
try { chmodSync(path, 0o600); } catch { /* best-effort */ }
|
|
55
58
|
if (process.platform === "win32") {
|
|
59
|
+
// Preserve the ACL helper's metadata observation across reads. Windows chmod does
|
|
60
|
+
// not establish a private DACL and can invalidate that observation before every load.
|
|
56
61
|
hardenSecretPath(path, { required: false });
|
|
62
|
+
} else {
|
|
63
|
+
try { chmodSync(path, 0o600); } catch { /* best-effort */ }
|
|
57
64
|
}
|
|
58
65
|
}
|
|
59
66
|
}
|
|
@@ -127,9 +134,14 @@ export function warnDegradedCompactionRouting(rawParsed: unknown, validated: Ocx
|
|
|
127
134
|
* the ratchet only ever moves down: a per-block call there costs a line the file does not have.
|
|
128
135
|
*/
|
|
129
136
|
export function warnDegradedTopLevelOptIns(rawParsed: unknown, validated: OcxConfig): void {
|
|
137
|
+
if (nativeReasoningRetentionConfigError(rawParsed)) console.warn("⚠️ invalid nativeReasoningRetention disabled; model and account switch allowances remain off");
|
|
130
138
|
setCodexWsReuseAcrossTurns(validated.codexWsReuseAcrossTurns === true);
|
|
139
|
+
const retentionWarning = degradedReasoningRetentionWarning(rawParsed, validated);
|
|
140
|
+
if (retentionWarning) console.warn(retentionWarning);
|
|
131
141
|
if (compactionRecoveryConfigError(rawParsed)) console.warn("⚠️ invalid compactionRecovery disabled; the original compaction failure is preserved");
|
|
142
|
+
if (desktopCompatibilityConfigError(rawParsed)) console.warn("Invalid desktopCompatibility startup preference ignored; no desktop compatibility service will start automatically.");
|
|
132
143
|
if (blockedModelRedirectsError(rawParsed)) console.warn("⚠️ invalid blockedModelRedirects ignored; provider routing remains available");
|
|
144
|
+
if (mainAccountHardLockFallbackError(rawParsed)) console.warn("⚠️ invalid codexMainAccountHardLockFallback ignored; the main-account hard lock still refuses while blocked");
|
|
133
145
|
const chatgptDesktop = chatgptDesktopConfigIssue(rawParsed);
|
|
134
146
|
if (chatgptDesktop) console.warn(`⚠️ config.json ${chatgptDesktop} — the whole chatgptDesktop block is ignored, so the ChatGPT desktop integration reads as off`);
|
|
135
147
|
warnDegradedStreamMode(rawParsed, validated);
|
|
@@ -142,6 +154,11 @@ export function warnDegradedTopLevelOptIns(rawParsed: unknown, validated: OcxCon
|
|
|
142
154
|
}
|
|
143
155
|
}
|
|
144
156
|
|
|
157
|
+
export function degradedReasoningRetentionWarning(rawParsed: unknown, validated: OcxConfig): string | null {
|
|
158
|
+
if (rawConfigRecord(rawParsed)?.reasoningRetention === undefined || validated.reasoningRetention !== undefined) return null;
|
|
159
|
+
return "config.json reasoningRetention is invalid — reasoning retention is disabled";
|
|
160
|
+
}
|
|
161
|
+
|
|
145
162
|
/**
|
|
146
163
|
* A malformed `memoryModels` phase disables that phase rather than failing the whole schema, so
|
|
147
164
|
* say so once: silently keeping whatever route the phase already had — which may be the shadow
|
|
@@ -406,6 +423,11 @@ export function sanitizeModelCostsForLoad(parsed: unknown): void {
|
|
|
406
423
|
export function sanitizeAutoReviewForLoad(parsed: unknown): void {
|
|
407
424
|
if (!parsed || typeof parsed !== "object") return;
|
|
408
425
|
const root = parsed as Record<string, unknown>;
|
|
426
|
+
if (root.autoReviewOverride !== undefined
|
|
427
|
+
&& autoReviewOverrideConfigError(root.autoReviewOverride) !== null) {
|
|
428
|
+
console.warn("⚠️ config.json autoReviewOverride is invalid — ignoring the override");
|
|
429
|
+
delete root.autoReviewOverride;
|
|
430
|
+
}
|
|
409
431
|
const providers = root.providers;
|
|
410
432
|
if (!providers || typeof providers !== "object" || Array.isArray(providers)) return;
|
|
411
433
|
for (const [name, providerValue] of Object.entries(providers as Record<string, unknown>)) {
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { OPENCODE_GO_LEGACY_NAME_PINS } from "../types/opencode-go";
|
|
2
|
+
import { captureWireAdapterHardPins } from "../types/wire";
|
|
3
|
+
import { declaredUnsupportedHostedTools, isHostedToolUnsupportedForModel } from "../responses/hosted-tool-policy";
|
|
4
|
+
import { sanitizeOpenCodeZenOverridesForLoad } from "./opencode-zen-upgrade";
|
|
5
|
+
|
|
6
|
+
function record(value: unknown): Record<string, unknown> | undefined {
|
|
7
|
+
return value !== null && typeof value === "object" && !Array.isArray(value)
|
|
8
|
+
? value as Record<string, unknown> : undefined;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/** File reads alone repair overrides admitted before OpenCode's native-wire upgrade. */
|
|
12
|
+
export function sanitizeOpenCodeOverridesForLoad(parsed: unknown): void {
|
|
13
|
+
sanitizeOpenCodeZenOverridesForLoad(parsed);
|
|
14
|
+
const providers = record(record(parsed)?.providers);
|
|
15
|
+
const provider = providers && Object.hasOwn(providers, "opencode-go")
|
|
16
|
+
? record(providers["opencode-go"]) : undefined;
|
|
17
|
+
if (!provider) return;
|
|
18
|
+
// Use validation's destination/auth rules, including the Go preset's fixed URL.
|
|
19
|
+
const pins = captureWireAdapterHardPins("opencode-go", provider);
|
|
20
|
+
const newlyPinned = (model: string): boolean => Object.hasOwn(pins, model)
|
|
21
|
+
&& !OPENCODE_GO_LEGACY_NAME_PINS.has(model);
|
|
22
|
+
const overrides = record(provider.modelAdapters) ?? {};
|
|
23
|
+
const preferences = record(provider.modelPreferHostedTools);
|
|
24
|
+
let preferencesChanged = false;
|
|
25
|
+
if (preferences) {
|
|
26
|
+
const unsupported = declaredUnsupportedHostedTools(provider as { unsupportedHostedTools?: readonly string[] });
|
|
27
|
+
for (const [model, tools] of Object.entries(preferences)) {
|
|
28
|
+
// Go's registry owns the old provider-wide Chat wire. Only an explicit
|
|
29
|
+
// Responses override could previously make these native models eligible.
|
|
30
|
+
if (!newlyPinned(model.trim()) || overrides[model] !== "openai-responses"
|
|
31
|
+
|| !Array.isArray(tools) || tools.length === 0 || tools.some(tool => tool !== "image_generation")
|
|
32
|
+
|| isHostedToolUnsupportedForModel(model, "image_generation", undefined, unsupported)) continue;
|
|
33
|
+
delete preferences[model];
|
|
34
|
+
preferencesChanged = true;
|
|
35
|
+
}
|
|
36
|
+
if (preferencesChanged && Object.keys(preferences).length === 0) delete provider.modelPreferHostedTools;
|
|
37
|
+
}
|
|
38
|
+
let overridesChanged = false;
|
|
39
|
+
for (const [model, adapter] of Object.entries(overrides)) {
|
|
40
|
+
// Leave malformed or previously incompatible preferences visible on every pass.
|
|
41
|
+
if (preferences && Object.hasOwn(preferences, model)) continue;
|
|
42
|
+
if (newlyPinned(model.trim()) && (adapter === "openai-chat" || adapter === "openai-responses")) {
|
|
43
|
+
delete overrides[model];
|
|
44
|
+
overridesChanged = true;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (overridesChanged) {
|
|
48
|
+
if (Object.keys(overrides).length === 0) delete provider.modelAdapters;
|
|
49
|
+
console.warn("OpenCode Go native models now use their required APIs; obsolete Chat/Responses overrides were ignored while loading config.json.");
|
|
50
|
+
}
|
|
51
|
+
if (preferencesChanged) {
|
|
52
|
+
console.warn("OpenCode Go native models now use their required APIs; legacy Responses-only hosted-tool preferences were ignored while loading config.json.");
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { openCodeZenBaseUrl } from "../types/opencode-zen";
|
|
2
|
+
import { declaredUnsupportedHostedTools, isHostedToolUnsupportedForModel } from "../responses/hosted-tool-policy";
|
|
3
|
+
|
|
4
|
+
function record(value: unknown): Record<string, unknown> | undefined {
|
|
5
|
+
return value !== null && typeof value === "object" && !Array.isArray(value)
|
|
6
|
+
? value as Record<string, unknown> : undefined;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function isGeminiModel(model: string): boolean {
|
|
10
|
+
return model.trim().toLowerCase().startsWith("gemini-");
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** Repair legacy Zen wire overrides and dependent preferences in file-backed reads only. */
|
|
14
|
+
export function sanitizeOpenCodeZenOverridesForLoad(parsed: unknown): void {
|
|
15
|
+
const providers = record(record(parsed)?.providers);
|
|
16
|
+
const provider = providers && Object.hasOwn(providers, "opencode-zen")
|
|
17
|
+
? record(providers["opencode-zen"]) : undefined;
|
|
18
|
+
if (!provider || !openCodeZenBaseUrl(provider)) return;
|
|
19
|
+
const overrides = record(provider.modelAdapters) ?? {};
|
|
20
|
+
const preferences = record(provider.modelPreferHostedTools);
|
|
21
|
+
let preferencesChanged = false;
|
|
22
|
+
if (preferences) {
|
|
23
|
+
const unsupported = declaredUnsupportedHostedTools(provider as { unsupportedHostedTools?: readonly string[] });
|
|
24
|
+
for (const [model, tools] of Object.entries(preferences)) {
|
|
25
|
+
// Capture the old wire before deleting overrides. An explicit non-Responses entry
|
|
26
|
+
// must not inherit a provider-wide Responses default for this migration.
|
|
27
|
+
const oldAdapter = Object.hasOwn(overrides, model) ? overrides[model] : provider.adapter;
|
|
28
|
+
if (!isGeminiModel(model) || oldAdapter !== "openai-responses"
|
|
29
|
+
|| !Array.isArray(tools) || tools.length === 0 || tools.some(tool => tool !== "image_generation")
|
|
30
|
+
|| isHostedToolUnsupportedForModel(model, "image_generation", undefined, unsupported)) continue;
|
|
31
|
+
delete preferences[model];
|
|
32
|
+
preferencesChanged = true;
|
|
33
|
+
}
|
|
34
|
+
if (preferencesChanged && Object.keys(preferences).length === 0) delete provider.modelPreferHostedTools;
|
|
35
|
+
}
|
|
36
|
+
let overridesChanged = false;
|
|
37
|
+
for (const [model, adapter] of Object.entries(overrides)) {
|
|
38
|
+
// Retain invalid preference/wire pairs so repeated normalization cannot admit them.
|
|
39
|
+
if (preferences && Object.hasOwn(preferences, model)) continue;
|
|
40
|
+
if (isGeminiModel(model) && (adapter === "openai-chat" || adapter === "openai-responses")) {
|
|
41
|
+
delete overrides[model];
|
|
42
|
+
overridesChanged = true;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
if (overridesChanged) {
|
|
46
|
+
if (Object.keys(overrides).length === 0) delete provider.modelAdapters;
|
|
47
|
+
console.warn("OpenCode Zen Gemini models now use the native Google API; obsolete Chat/Responses overrides were ignored while loading config.json.");
|
|
48
|
+
}
|
|
49
|
+
if (preferencesChanged) {
|
|
50
|
+
console.warn("OpenCode Zen Gemini models now use the native Google API; legacy Responses-only hosted-tool preferences were ignored while loading config.json.");
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -24,21 +24,19 @@
|
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
26
|
import { createHash } from "node:crypto";
|
|
27
|
-
import { chmodSync, existsSync, lstatSync, mkdirSync, readdirSync, readFileSync, statSync, unlinkSync } from "node:fs";
|
|
27
|
+
import { chmodSync, existsSync, lstatSync, mkdirSync, readdirSync, readFileSync, realpathSync, statSync, unlinkSync } from "node:fs";
|
|
28
28
|
import { homedir } from "node:os";
|
|
29
29
|
import { dirname, join, resolve } from "node:path";
|
|
30
30
|
import { atomicWriteFileNoFollow } from "./atomic-write";
|
|
31
31
|
import { getConfigDir } from "./paths";
|
|
32
|
-
import { assertNotRealHomeUnderTest } from "../lib/test-home-guard";
|
|
32
|
+
import { assertNotRealHomeUnderTest, isTestHomeGuardArmed } from "../lib/test-home-guard";
|
|
33
33
|
import { hardenSecretDir } from "../lib/windows-secret-acl";
|
|
34
34
|
|
|
35
35
|
const REGISTRY_DIR_NAME = "ocx-homes";
|
|
36
36
|
/**
|
|
37
|
-
* Test seam, not a user knob
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
* fixture home; production always leaves it unset so the default home stays the
|
|
41
|
-
* one locator every sibling can find.
|
|
37
|
+
* Test seam, not a user knob. The sandbox pins this for inherited child environments;
|
|
38
|
+
* guarded callers without it use their selected config home. Production leaves it
|
|
39
|
+
* unset so the default home stays the one locator every sibling can find.
|
|
42
40
|
*/
|
|
43
41
|
const REGISTRY_DIR_ENV = "OCX_OWNER_REGISTRY_DIR";
|
|
44
42
|
const REGISTRY_ENTRY_SUFFIX = ".json";
|
|
@@ -60,8 +58,23 @@ function registryBaseDir(): string {
|
|
|
60
58
|
/** The shared directory the registry lives under. */
|
|
61
59
|
export function ownerRegistryDir(): string {
|
|
62
60
|
const override = process.env[REGISTRY_DIR_ENV]?.trim();
|
|
63
|
-
|
|
64
|
-
|
|
61
|
+
const dir = override ? resolve(override)
|
|
62
|
+
: join(isTestHomeGuardArmed() ? getConfigDir() : registryBaseDir(), REGISTRY_DIR_NAME);
|
|
63
|
+
// Bun caches the startup home, so HOME rewrites cannot isolate this anchor.
|
|
64
|
+
// Check reads and removals too, including an inherited override or symlink that
|
|
65
|
+
// points back into the protected home. An unpinned guarded caller fails closed.
|
|
66
|
+
if (isTestHomeGuardArmed()) {
|
|
67
|
+
for (let ancestor = dir; ;) {
|
|
68
|
+
assertNotRealHomeUnderTest(ancestor);
|
|
69
|
+
// Follow an existing alias before walking parents; missing children reach
|
|
70
|
+
// their nearest existing ancestor on the next iterations.
|
|
71
|
+
try { ancestor = realpathSync.native(ancestor); } catch { /* not created yet */ }
|
|
72
|
+
const parent = dirname(ancestor);
|
|
73
|
+
if (parent === ancestor) break;
|
|
74
|
+
ancestor = parent;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return dir;
|
|
65
78
|
}
|
|
66
79
|
|
|
67
80
|
function registryEntryPath(dir: string, home: string): string {
|
|
@@ -76,14 +89,6 @@ function registryEntryPath(dir: string, home: string): string {
|
|
|
76
89
|
export function registerOwnerRegistryHome(home: string): void {
|
|
77
90
|
try {
|
|
78
91
|
const dir = ownerRegistryDir();
|
|
79
|
-
// Guard every ancestor of the write target: an override that still resolves
|
|
80
|
-
// under the protected ~/.opencodex must not slip past the test home guard.
|
|
81
|
-
for (let ancestor = dir; ;) {
|
|
82
|
-
assertNotRealHomeUnderTest(ancestor);
|
|
83
|
-
const parent = dirname(ancestor);
|
|
84
|
-
if (parent === ancestor) break;
|
|
85
|
-
ancestor = parent;
|
|
86
|
-
}
|
|
87
92
|
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
88
93
|
// This locator contains no secret, but its deterministic entry names must not
|
|
89
94
|
// give another local user a place to plant a destination symlink. Refuse a
|
package/src/config/paths.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { join, resolve } from "node:path";
|
|
|
4
4
|
import { flushWindowsSecretAclReapsBeforeRemoval, hardenSecretDirAsync, windowsSecretAclApplies } from "../lib/windows-secret-acl";
|
|
5
5
|
import { assertNotRealHomeUnderTest } from "../lib/test-home-guard";
|
|
6
6
|
import { isPassiveRead } from "../lib/passive-read";
|
|
7
|
+
import { trackBackgroundWork } from "../lib/background-work";
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* Expand a leading `~` in user-supplied paths without interpreting shell
|
|
@@ -37,17 +38,23 @@ export function hardenConfigDir(): void {
|
|
|
37
38
|
// have changed the protected directory used by the test-home boundary.
|
|
38
39
|
assertNotRealHomeUnderTest(dir);
|
|
39
40
|
if (!existsSync(dir)) return;
|
|
40
|
-
|
|
41
|
+
// Windows privacy is enforced by NTFS ACLs. chmod only changes DOS attributes there;
|
|
42
|
+
// doing it before the ACL lookup can invalidate its ctime-based successful observation.
|
|
43
|
+
if (!windowsSecretAclApplies()) {
|
|
44
|
+
try { chmodSync(dir, 0o700); } catch { /* best-effort */ }
|
|
45
|
+
}
|
|
41
46
|
if (windowsSecretAclApplies() && !configDirHardeningFlights.has(dir)) {
|
|
42
47
|
// This is an optional read-path harden. Waiting synchronously here used to stop the Bun
|
|
43
48
|
// event loop (including /healthz) for the full icacls timeout. Required mutation paths keep
|
|
44
49
|
// their own awaited/fail-closed hardening; ordinary config reads only start one soft flight.
|
|
45
|
-
|
|
50
|
+
// Tracked: icacls holds the directory open, and a config write or removal that lands on
|
|
51
|
+
// it before the child exits fails with a sharing violation on Windows.
|
|
52
|
+
const flight = trackBackgroundWork("config-dir-acl-harden", hardenSecretDirAsync(dir, { required: false })
|
|
46
53
|
.then(() => undefined)
|
|
47
54
|
.catch(() => undefined)
|
|
48
55
|
.finally(() => {
|
|
49
56
|
if (configDirHardeningFlights.get(dir) === flight) configDirHardeningFlights.delete(dir);
|
|
50
|
-
});
|
|
57
|
+
}));
|
|
51
58
|
configDirHardeningFlights.set(dir, flight);
|
|
52
59
|
}
|
|
53
60
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { isCanonicalOpenAiForwardProvider } from "../providers/openai-tiers";
|
|
2
|
+
import { resolveDshAccountMessagesUrl } from "../providers/dsh-account-destination";
|
|
2
3
|
import { redactSecretString } from "../lib/redact";
|
|
3
4
|
import {
|
|
4
5
|
isValidModelDiscoveryModelId,
|
|
@@ -256,10 +257,11 @@ export function autoReviewModelTargetConfigError(
|
|
|
256
257
|
field = "autoReviewModel",
|
|
257
258
|
allowClear = false,
|
|
258
259
|
): string | null {
|
|
259
|
-
if (value === undefined || (allowClear &&
|
|
260
|
+
if (value === undefined || (allowClear && value === null)) return null;
|
|
260
261
|
if (typeof value !== "string") return `${field} must be a string`;
|
|
261
262
|
const trimmed = value.trim();
|
|
262
|
-
|
|
263
|
+
// `allowClear` callers trim then delete, so whitespace-only is a clear, not a blank selector.
|
|
264
|
+
if (!trimmed) return allowClear ? null : `${field} must be nonblank`;
|
|
263
265
|
if (trimmed.length > 1024 || AUTO_REVIEW_MODEL_CONTROL_CHARS.test(trimmed)) {
|
|
264
266
|
return `${field} must be a catalog selector without whitespace or control characters`;
|
|
265
267
|
}
|
|
@@ -271,6 +273,26 @@ export function isValidAutoReviewModel(value: unknown): value is string {
|
|
|
271
273
|
return typeof value === "string" && autoReviewModelTargetConfigError(value) === null;
|
|
272
274
|
}
|
|
273
275
|
|
|
276
|
+
/**
|
|
277
|
+
* Validate the global auto-review override block the Models settings panel writes.
|
|
278
|
+
*
|
|
279
|
+
* `enabled` is optional so a save may carry only the model during an edit; `model` follows the
|
|
280
|
+
* same selector rules as the provider-scoped field, and an empty value clears the override.
|
|
281
|
+
*/
|
|
282
|
+
export function autoReviewOverrideConfigError(value: unknown, field = "autoReviewOverride"): string | null {
|
|
283
|
+
if (value === undefined || value === null) return null;
|
|
284
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return `${field} must be a plain object`;
|
|
285
|
+
const prototype = Object.getPrototypeOf(value);
|
|
286
|
+
if (prototype !== Object.prototype && prototype !== null) {
|
|
287
|
+
return `${field} must be a plain object with own properties`;
|
|
288
|
+
}
|
|
289
|
+
const block = value as Record<string, unknown>;
|
|
290
|
+
if (block.enabled !== undefined && typeof block.enabled !== "boolean") {
|
|
291
|
+
return `${field}.enabled must be a boolean`;
|
|
292
|
+
}
|
|
293
|
+
return autoReviewModelTargetConfigError(block.model, `${field}.model`, true);
|
|
294
|
+
}
|
|
295
|
+
|
|
274
296
|
/** Canonical model key used for map matching, duplicate detection, and route tombstones. */
|
|
275
297
|
export function canonicalAutoReviewModelKey(modelId: string): string {
|
|
276
298
|
return encodeRoutedModelId(modelId.trim());
|
|
@@ -390,6 +412,27 @@ export function modelAdapterRecordConfigError(
|
|
|
390
412
|
}
|
|
391
413
|
|
|
392
414
|
|
|
415
|
+
/**
|
|
416
|
+
* The `dsh-account` row owns the DeepSeek Harness account token, which every helper resolves by this
|
|
417
|
+
* provider name. Keeping the row on its own adapter and DeepSeek's Anthropic host means no generic
|
|
418
|
+
* helper (Anthropic sidecars, discovery, probes) can be pointed at another destination with it.
|
|
419
|
+
*/
|
|
420
|
+
export function credentialPinnedProviderConfigError(
|
|
421
|
+
providerName: string,
|
|
422
|
+
provider: { adapter?: unknown; baseUrl?: unknown },
|
|
423
|
+
): string | null {
|
|
424
|
+
if (providerName !== "dsh-account") return null;
|
|
425
|
+
if (provider.adapter !== undefined && provider.adapter !== "dsh-account") {
|
|
426
|
+
return 'adapter must stay "dsh-account": its account token is pinned to that adapter';
|
|
427
|
+
}
|
|
428
|
+
if (typeof provider.baseUrl === "string") {
|
|
429
|
+
try { resolveDshAccountMessagesUrl(provider.baseUrl); } catch {
|
|
430
|
+
return "baseUrl must stay on https://api.deepseek.com/anthropic: its account token is pinned to that host";
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
return null;
|
|
434
|
+
}
|
|
435
|
+
|
|
393
436
|
function capabilityRecord(value: unknown): value is Record<string, unknown> {
|
|
394
437
|
return value !== null && typeof value === "object" && !Array.isArray(value)
|
|
395
438
|
&& [Object.prototype, null].includes(Object.getPrototypeOf(value));
|