@iislee/opencodex 2.11.0 → 2.31.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/AGENTS_INSTALL.md +109 -0
- package/README.md +114 -19
- package/bin/ocx.mjs +164 -36
- package/bin/package-main.mjs +1 -1
- package/gui/dist/assets/index-BF38heuV.js +104 -0
- package/gui/dist/assets/index-DMiI18Kv.css +1 -0
- package/gui/dist/index.html +2 -2
- package/gui/dist/provider-icons/alibaba-color.svg +1 -1
- package/gui/dist/provider-icons/antigravity-color.svg +1 -1
- package/gui/dist/provider-icons/claude-color.svg +1 -1
- package/gui/dist/provider-icons/cline-color.svg +16 -0
- package/gui/dist/provider-icons/cloudflare-ai-gateway-color.svg +1 -1
- package/gui/dist/provider-icons/commandcode-color.svg +1 -0
- package/gui/dist/provider-icons/copilot-color.svg +1 -1
- package/gui/dist/provider-icons/cursor-color.svg +1 -1
- package/gui/dist/provider-icons/deepseek-color.svg +1 -1
- package/gui/dist/provider-icons/firepass-color.svg +1 -1
- package/gui/dist/provider-icons/fireworks-color.svg +1 -1
- package/gui/dist/provider-icons/gemini-color.svg +1 -1
- package/gui/dist/provider-icons/github-copilot-color.svg +1 -1
- package/gui/dist/provider-icons/gitlab-duo-color.svg +1 -1
- package/gui/dist/provider-icons/grok.svg +1 -1
- package/gui/dist/provider-icons/groq-color.svg +1 -1
- package/gui/dist/provider-icons/huggingface-color.svg +1 -1
- package/gui/dist/provider-icons/kimi-color.svg +1 -1
- package/gui/dist/provider-icons/kiro-color.svg +2 -2
- package/gui/dist/provider-icons/lm-studio-color.svg +1 -1
- package/gui/dist/provider-icons/mistral-color.svg +1 -1
- package/gui/dist/provider-icons/moonshot-color.svg +1 -1
- package/gui/dist/provider-icons/nvidia-color.svg +1 -1
- package/gui/dist/provider-icons/ollama-color.svg +1 -1
- package/gui/dist/provider-icons/openai.svg +1 -1
- package/gui/dist/provider-icons/opencode.svg +2 -1
- package/gui/dist/provider-icons/openrouter-color.svg +1 -1
- package/gui/dist/provider-icons/pi.svg +2 -2
- package/gui/dist/provider-icons/qianfan-color.svg +1 -1
- package/gui/dist/provider-icons/qwen-portal-color.svg +1 -1
- package/gui/dist/provider-icons/vercel-ai-gateway-color.svg +1 -1
- package/gui/dist/provider-icons/vllm-color.svg +1 -1
- package/gui/dist/provider-icons/xiaomi-color.svg +1 -1
- package/package.json +19 -10
- package/src/adapters/anthropic-output-schema.ts +137 -0
- package/src/adapters/anthropic.ts +376 -52
- package/src/adapters/base.ts +54 -7
- package/src/adapters/client-fingerprint.ts +18 -12
- package/src/adapters/cline-pass-deepseek-v4-tool-replay.ts +69 -0
- package/src/adapters/command-code.ts +601 -0
- package/src/adapters/cursor/checkpoint-store.ts +303 -0
- package/src/adapters/cursor/cursor-errors.ts +129 -6
- package/src/adapters/cursor/discovery.ts +61 -4
- package/src/adapters/cursor/effort-map.ts +27 -3
- package/src/adapters/cursor/framing.ts +39 -0
- package/src/adapters/cursor/h2-pool.ts +123 -0
- package/src/adapters/cursor/http1-bidi.ts +361 -0
- package/src/adapters/cursor/images.ts +704 -0
- package/src/adapters/cursor/live-models.ts +174 -58
- package/src/adapters/cursor/live-transport.ts +609 -170
- package/src/adapters/cursor/native-exec-common.ts +23 -2
- package/src/adapters/cursor/native-exec-fs.ts +10 -7
- package/src/adapters/cursor/native-exec-network.ts +1 -1
- package/src/adapters/cursor/native-exec-shell.ts +0 -1
- package/src/adapters/cursor/native-exec.ts +101 -14
- package/src/adapters/cursor/protobuf-events.ts +829 -11
- package/src/adapters/cursor/protobuf-request.ts +383 -65
- package/src/adapters/cursor/request-builder.ts +237 -31
- package/src/adapters/cursor/tool-definitions.ts +242 -12
- package/src/adapters/cursor/tool-result-normalize.ts +92 -0
- package/src/adapters/cursor/transport.ts +22 -0
- package/src/adapters/cursor/types.ts +28 -1
- package/src/adapters/cursor.ts +133 -7
- package/src/adapters/google-antigravity-replay.ts +558 -34
- package/src/adapters/google-antigravity-wire.ts +43 -10
- package/src/adapters/google-http.ts +38 -10
- package/src/adapters/google-tool-schema.ts +80 -15
- package/src/adapters/google-truncation.ts +11 -0
- package/src/adapters/google.ts +618 -74
- package/src/adapters/identity.ts +39 -6
- package/src/adapters/kiro-errors.ts +11 -0
- package/src/adapters/kiro-events.ts +19 -1
- package/src/adapters/kiro-thinking.ts +18 -2
- package/src/adapters/kiro-tools.ts +10 -1
- package/src/adapters/kiro.ts +92 -54
- package/src/adapters/mimo-free.ts +17 -0
- package/src/adapters/openai-chat-url.ts +11 -0
- package/src/adapters/openai-chat.ts +1311 -336
- package/src/adapters/openai-responses-url.ts +16 -0
- package/src/adapters/openai-responses.ts +830 -56
- package/src/adapters/registry.ts +175 -0
- package/src/adapters/responses-tool-schema.ts +67 -0
- package/src/adapters/tool-call-id.ts +119 -0
- package/src/adapters/tool-catalog-nudge.ts +104 -21
- package/src/adapters/xai-web-search.ts +185 -0
- package/src/bridge.ts +428 -61
- package/src/chat/inbound.ts +43 -19
- package/src/chat/outbound.ts +82 -26
- package/src/claude/agents-inject.ts +32 -9
- package/src/claude/context-windows.ts +21 -5
- package/src/claude/desktop-3p.ts +243 -9
- package/src/claude/gateway-cache.ts +41 -4
- package/src/claude/inbound.ts +72 -3
- package/src/claude/model-info.ts +38 -15
- package/src/claude/outbound.ts +70 -16
- package/src/cli/account-api.ts +35 -1
- package/src/cli/account-auth.ts +33 -6
- package/src/cli/account-catalog-refresh.ts +14 -0
- package/src/cli/account-extended.ts +389 -2
- package/src/cli/account-main.ts +317 -0
- package/src/cli/account.ts +30 -6
- package/src/cli/agent.ts +45 -1
- package/src/cli/claude-agent-startup-sync.ts +73 -0
- package/src/cli/claude-desktop.ts +29 -4
- package/src/cli/claude.ts +103 -34
- package/src/cli/codex-log-guard-doctor.ts +103 -0
- package/src/cli/codex-shim-autorestore.ts +2 -0
- package/src/cli/codex-shim-readiness.ts +76 -0
- package/src/cli/combo.ts +8 -0
- package/src/cli/config-command.ts +74 -10
- package/src/cli/dispatch.ts +593 -0
- package/src/cli/doctor.ts +315 -43
- package/src/cli/ensure-desired-integrations.ts +152 -0
- package/src/cli/export-command.ts +46 -20
- package/src/cli/help.ts +20 -271
- package/src/cli/index.ts +378 -519
- package/src/cli/init.ts +4 -17
- package/src/cli/integrations.ts +120 -2
- package/src/cli/lab.ts +607 -0
- package/src/cli/launcher-context.ts +77 -0
- package/src/cli/minimax.ts +497 -0
- package/src/cli/models-runtime.ts +35 -2
- package/src/cli/models.ts +100 -14
- package/src/cli/observe.ts +92 -3
- package/src/cli/opencode.ts +4 -2
- package/src/cli/provider-runtime.ts +18 -1
- package/src/cli/provider.ts +24 -3
- package/src/cli/ready.ts +301 -0
- package/src/cli/registry.ts +437 -0
- package/src/cli/root.ts +86 -0
- package/src/cli/route-policy.ts +92 -0
- package/src/cli/runtime-api.ts +6 -3
- package/src/cli/star-prompt.ts +71 -15
- package/src/cli/status.ts +10 -3
- package/src/cli/system-restart-client.ts +146 -0
- package/src/cli/tray-proxy.ts +153 -6
- package/src/cli/v2.ts +105 -10
- package/src/cli.ts +1 -1
- package/src/clients/config-export.ts +1358 -21
- package/src/codex/account-label.ts +14 -1
- package/src/codex/account-lifecycle.ts +130 -13
- package/src/codex/account-namespaces.ts +49 -3
- package/src/codex/account-priority.ts +83 -0
- package/src/codex/account-store.ts +29 -2
- package/src/codex/account-usability.ts +25 -2
- package/src/codex/admission.ts +256 -0
- package/src/codex/affinity-debug.ts +162 -0
- package/src/codex/app-server-processes.ts +493 -106
- package/src/codex/app-server-restart-service.ts +232 -0
- package/src/codex/auth-api.ts +849 -242
- package/src/codex/auth-collision.ts +5 -3
- package/src/codex/auth-context.ts +345 -32
- package/src/codex/autostart-health.ts +8 -1
- package/src/codex/catalog/account-models.ts +67 -0
- package/src/codex/catalog/aggregation.ts +68 -10
- package/src/codex/catalog/bundled.ts +331 -33
- package/src/codex/catalog/effort.ts +121 -30
- package/src/codex/catalog/filesystem-evidence.ts +302 -0
- package/src/codex/catalog/kinds.ts +2 -0
- package/src/codex/catalog/metadata.ts +529 -45
- package/src/codex/catalog/native-models.ts +72 -0
- package/src/codex/catalog/parsing.ts +224 -30
- package/src/codex/catalog/provider-fetch.ts +1460 -134
- package/src/codex/catalog/sync.ts +1449 -186
- package/src/codex/catalog-admission.ts +199 -0
- package/src/codex/catalog-refresh-status.ts +105 -0
- package/src/codex/catalog-write-serialization.ts +242 -0
- package/src/codex/catalog.ts +6 -3
- package/src/codex/codex-write-lock.ts +384 -0
- package/src/codex/convergence-types.ts +614 -0
- package/src/codex/convergence.ts +651 -0
- package/src/codex/coordinator-doctor.ts +332 -0
- package/src/codex/custom-model-catalog-migration.ts +176 -0
- package/src/codex/desired-state.ts +230 -0
- package/src/codex/features.ts +636 -39
- package/src/codex/generation.ts +202 -0
- package/src/codex/history-job.ts +407 -0
- package/src/codex/history-lock.ts +242 -0
- package/src/codex/history-migration-guardian.ts +26 -20
- package/src/codex/history-provider.ts +231 -28
- package/src/codex/history-transition.ts +105 -0
- package/src/codex/history-worker.ts +220 -0
- package/src/codex/inject-coordination.ts +290 -0
- package/src/codex/inject.ts +1073 -152
- package/src/codex/injected-marker.ts +37 -3
- package/src/codex/integration-record.ts +266 -0
- package/src/codex/internal/catalog-writer.ts +203 -0
- package/src/codex/internal/history-writer.ts +80 -0
- package/src/codex/journal.ts +66 -4
- package/src/codex/log-guard/inspect.ts +506 -0
- package/src/codex/log-guard/lock.ts +150 -0
- package/src/codex/log-guard/maintenance.ts +403 -0
- package/src/codex/log-guard/path-safety.ts +88 -0
- package/src/codex/log-guard/policy.ts +44 -0
- package/src/codex/log-guard/processes.ts +205 -0
- package/src/codex/log-guard/protection.ts +489 -0
- package/src/codex/log-guard/sqlite-errors.ts +9 -0
- package/src/codex/main-account-cache.ts +24 -0
- package/src/codex/main-account.ts +29 -1
- package/src/codex/management-convergence.ts +167 -0
- package/src/codex/model-cache.ts +56 -10
- package/src/codex/model-entitlements.ts +353 -0
- package/src/codex/native-main-admission.ts +47 -0
- package/src/codex/native-main-auth-temp.ts +187 -0
- package/src/codex/native-main-claim.ts +178 -0
- package/src/codex/native-main-lock-file.ts +162 -0
- package/src/codex/native-main-owner.ts +329 -0
- package/src/codex/native-profile-api.ts +247 -0
- package/src/codex/native-profile-manager.ts +1531 -0
- package/src/codex/native-profile-processes.ts +121 -0
- package/src/codex/native-profile-recovery.ts +99 -0
- package/src/codex/native-profile-stage-store.ts +387 -0
- package/src/codex/native-profile-startup.ts +492 -0
- package/src/codex/native-profile-store.ts +855 -0
- package/src/codex/native-profile-types.ts +120 -0
- package/src/codex/native-residue.ts +682 -0
- package/src/codex/paths.ts +80 -1
- package/src/codex/plan-from-token.ts +140 -0
- package/src/codex/plan.ts +40 -0
- package/src/codex/plugins-doctor.ts +1 -1
- package/src/codex/pool-rotation.ts +74 -4
- package/src/codex/project-config-warnings.ts +20 -6
- package/src/codex/prompt-journal.ts +352 -0
- package/src/codex/prompt-layers.ts +967 -0
- package/src/codex/prompt-lock.ts +143 -0
- package/src/codex/quota-rejection.ts +298 -0
- package/src/codex/quota.ts +175 -13
- package/src/codex/refresh.ts +11 -2
- package/src/codex/reset-credit-recovery.ts +1044 -0
- package/src/codex/routing.ts +505 -94
- package/src/codex/runtime.ts +159 -38
- package/src/codex/shim.ts +1009 -28
- package/src/codex/subagent-model-fallback.ts +350 -35
- package/src/codex/sync.ts +191 -2
- package/src/codex/transition-state.ts +612 -0
- package/src/codex/upstream-host-health.ts +368 -0
- package/src/codex/user-identity.ts +557 -0
- package/src/codex/warmup.ts +187 -81
- package/src/codex/write-coordination.ts +114 -0
- package/src/combos/failover.ts +20 -0
- package/src/combos/index.ts +4 -0
- package/src/combos/request.ts +32 -0
- package/src/combos/types.ts +81 -9
- package/src/config/provider-name.ts +24 -0
- package/src/config.ts +1762 -140
- package/src/generated/compatibility-version.json +3164 -0
- package/src/generated/{jawcode-model-metadata.ts → model-metadata.ts} +19 -17
- package/src/grok/inject.ts +16 -5
- package/src/grok/inspect.ts +45 -0
- package/src/grok/sync.ts +2 -2
- package/src/images/loop.ts +152 -29
- package/src/images/plan.ts +23 -13
- package/src/integrations/config-io.ts +269 -0
- package/src/integrations/journal.ts +315 -0
- package/src/integrations/merge.ts +135 -0
- package/src/integrations/mutation-flight.ts +71 -0
- package/src/integrations/native/ownership-preflight.ts +202 -0
- package/src/integrations/omp-yaml-source.ts +358 -0
- package/src/integrations/owned-refresh.ts +74 -0
- package/src/integrations/ownership.ts +111 -0
- package/src/integrations/registry.ts +159 -0
- package/src/integrations/serialize.ts +314 -0
- package/src/integrations/state.ts +361 -0
- package/src/integrations/store.ts +103 -0
- package/src/integrations/writer-lock.ts +98 -0
- package/src/integrations/writer.ts +691 -0
- package/src/lab/artifacts/sanitize.ts +586 -0
- package/src/lab/artifacts/secure-fs.ts +475 -0
- package/src/lab/artifacts/store.ts +310 -0
- package/src/lab/automation/budgets.ts +78 -0
- package/src/lab/automation/config-persistence.ts +256 -0
- package/src/lab/automation/constants.ts +39 -0
- package/src/lab/automation/cooldown.ts +103 -0
- package/src/lab/automation/dispatch.ts +211 -0
- package/src/lab/automation/index.ts +13 -0
- package/src/lab/automation/orchestrator.ts +499 -0
- package/src/lab/automation/persistence.ts +512 -0
- package/src/lab/automation/planner.ts +371 -0
- package/src/lab/automation/policy.ts +136 -0
- package/src/lab/automation/queue.ts +191 -0
- package/src/lab/automation/recovery.ts +24 -0
- package/src/lab/automation/route-context.ts +21 -0
- package/src/lab/automation/run-key.ts +44 -0
- package/src/lab/automation/runs-query.ts +34 -0
- package/src/lab/automation/types.ts +160 -0
- package/src/lab/conformance/assertion.ts +325 -0
- package/src/lab/conformance/digest.ts +22 -0
- package/src/lab/conformance/executor.ts +741 -0
- package/src/lab/conformance/fixture-provider.ts +27 -0
- package/src/lab/conformance/fixtures/live-v1-cases.json +175 -0
- package/src/lab/conformance/fixtures/protocol-v1-cases.json +461 -0
- package/src/lab/conformance/harness-budget.ts +47 -0
- package/src/lab/conformance/index.ts +5 -0
- package/src/lab/conformance/jcs.ts +64 -0
- package/src/lab/conformance/json-pointer.ts +39 -0
- package/src/lab/conformance/manifest.ts +180 -0
- package/src/lab/conformance/mcp-stub.ts +179 -0
- package/src/lab/conformance/negative-controls.ts +164 -0
- package/src/lab/conformance/observation.ts +355 -0
- package/src/lab/conformance/runner.ts +68 -0
- package/src/lab/conformance/sse-normalize.ts +59 -0
- package/src/lab/conformance/suite-manifest.ts +78 -0
- package/src/lab/conformance/types.ts +214 -0
- package/src/lab/constants.ts +126 -0
- package/src/lab/digest.ts +64 -0
- package/src/lab/events/errors.ts +9 -0
- package/src/lab/events/limits.ts +117 -0
- package/src/lab/events/types.ts +229 -0
- package/src/lab/events/validate.ts +781 -0
- package/src/lab/fabric/constants.ts +40 -0
- package/src/lab/fabric/executor.ts +492 -0
- package/src/lab/fabric/index.ts +80 -0
- package/src/lab/fabric/manifest.ts +222 -0
- package/src/lab/fabric/observe.ts +489 -0
- package/src/lab/fabric/patch.ts +79 -0
- package/src/lab/fabric/producer-child.ts +139 -0
- package/src/lab/fabric/producer-isolate.ts +276 -0
- package/src/lab/fabric/producer-protocol.ts +61 -0
- package/src/lab/fabric/scratch.ts +439 -0
- package/src/lab/fabric/subject.ts +106 -0
- package/src/lab/fabric/types.ts +134 -0
- package/src/lab/fabric/verifier.ts +98 -0
- package/src/lab/index.ts +54 -0
- package/src/lab/ledger/artifact-refs.ts +127 -0
- package/src/lab/ledger/invalidation.ts +136 -0
- package/src/lab/ledger/purge.ts +310 -0
- package/src/lab/ledger/store.ts +532 -0
- package/src/lab/live/credential-lease.ts +53 -0
- package/src/lab/live/destination.ts +155 -0
- package/src/lab/live/executor.ts +336 -0
- package/src/lab/live/inert-tools.ts +56 -0
- package/src/lab/live/manifest.ts +85 -0
- package/src/lab/live/mcp-loopback.ts +57 -0
- package/src/lab/live/runner.ts +19 -0
- package/src/lab/live/sandbox.ts +61 -0
- package/src/lab/live/suite-manifest.ts +41 -0
- package/src/lab/live/transport.ts +118 -0
- package/src/lab/live/types.ts +197 -0
- package/src/lab/observe/from-conformance.ts +301 -0
- package/src/lab/observe/from-live.ts +117 -0
- package/src/lab/paths.ts +153 -0
- package/src/lab/projection/rebuild.ts +495 -0
- package/src/lab/projection/schema.ts +135 -0
- package/src/lab/projection/verdicts.ts +474 -0
- package/src/lab/projection/verification.ts +412 -0
- package/src/lab/public/bundle.ts +217 -0
- package/src/lab/public/community-authority.ts +175 -0
- package/src/lab/public/community-files.ts +29 -0
- package/src/lab/public/community.ts +479 -0
- package/src/lab/public/file-safety.ts +155 -0
- package/src/lab/public/ids.ts +26 -0
- package/src/lab/public/index.ts +16 -0
- package/src/lab/public/mutation-lock.ts +424 -0
- package/src/lab/public/operator.ts +353 -0
- package/src/lab/public/origin-purge.ts +79 -0
- package/src/lab/public/origin.ts +203 -0
- package/src/lab/public/privacy.ts +143 -0
- package/src/lab/public/private-file.ts +261 -0
- package/src/lab/public/project.ts +124 -0
- package/src/lab/public/purge-test-fault.ts +21 -0
- package/src/lab/public/purge.ts +223 -0
- package/src/lab/public/registry.ts +44 -0
- package/src/lab/public/revocation.ts +252 -0
- package/src/lab/public/signature.ts +243 -0
- package/src/lab/public/storage.ts +105 -0
- package/src/lab/public/strict-json.ts +206 -0
- package/src/lab/public/time.ts +26 -0
- package/src/lab/public/types.ts +172 -0
- package/src/lab/public/validate.ts +391 -0
- package/src/lab/query/catalog.ts +101 -0
- package/src/lab/query/connection.ts +107 -0
- package/src/lab/query/constants.ts +4 -0
- package/src/lab/query/cursor.ts +132 -0
- package/src/lab/query/dto-map.ts +277 -0
- package/src/lab/query/errors.ts +22 -0
- package/src/lab/query/freshness.ts +53 -0
- package/src/lab/query/index.ts +45 -0
- package/src/lab/query/latest-observation.ts +59 -0
- package/src/lab/query/passive-production.ts +159 -0
- package/src/lab/query/queries.ts +444 -0
- package/src/lab/query/types.ts +266 -0
- package/src/lab/subject/behavior-fingerprint.ts +77 -0
- package/src/lab/subject/installation-salt.ts +112 -0
- package/src/lab/subject/protocol-subject.ts +80 -0
- package/src/lab/subject/route-subject.ts +74 -0
- package/src/lib/app-owned-memory-stores.ts +22 -0
- package/src/lib/bounded-body.ts +153 -9
- package/src/lib/bun-runtime.ts +125 -12
- package/src/lib/bun-stream-caps.ts +13 -9
- package/src/lib/codex-restart-contract.ts +120 -0
- package/src/lib/config-ownership.ts +6 -2
- package/src/lib/destination-policy.ts +65 -1
- package/src/lib/errors.ts +44 -2
- package/src/lib/fabric-task-execution-authority.ts +7 -0
- package/src/lib/fabric-task-host.ts +29 -0
- package/src/lib/lab-activation.ts +223 -0
- package/src/lib/lab-live-execution-authority.ts +13 -0
- package/src/lib/lab-live-host.ts +30 -0
- package/src/lib/lab-live-pinned-sender.ts +56 -0
- package/src/lib/lab-live-route-production.ts +130 -0
- package/src/lib/lab-passive-linker-registration.ts +26 -0
- package/src/lib/local-management-attestation.ts +51 -0
- package/src/lib/local-management-capability.ts +100 -0
- package/src/lib/local-provider-reload-contract.ts +100 -0
- package/src/lib/optional-shutdown-hooks.ts +57 -0
- package/src/lib/pinned-http.ts +145 -26
- package/src/lib/process-control.ts +4 -1
- package/src/lib/provider-outbound.ts +49 -9
- package/src/lib/redact.ts +419 -3
- package/src/lib/self-launch-argv.ts +15 -0
- package/src/lib/server-resource-ownership.ts +71 -0
- package/src/lib/shadow-call.ts +35 -4
- package/src/lib/sse-decoder.ts +41 -0
- package/src/lib/state-store-registrations.ts +10 -2
- package/src/lib/system-restart-contract.ts +73 -0
- package/src/lib/token-estimate.ts +19 -2
- package/src/lib/tool-argument-integers.ts +202 -0
- package/src/lib/translator-budget.ts +44 -0
- package/src/lib/upstream-http-version.ts +57 -0
- package/src/lib/upstream-reachability.ts +95 -0
- package/src/lib/upstream-retry.ts +156 -3
- package/src/lib/windows-atomic-replace.ts +155 -0
- package/src/lib/windows-elevation.ts +70 -2
- package/src/lib/windows-secret-acl.ts +409 -69
- package/src/lib/windows-service-wrappers.ts +72 -0
- package/src/lib/windows-text.ts +106 -0
- package/src/lib/windows-user-principal.ts +341 -0
- package/src/lib/winsw.ts +33 -5
- package/src/oauth/account-import/google-antigravity-adapter.ts +74 -0
- package/src/oauth/account-import/index.ts +15 -0
- package/src/oauth/account-import/parser.ts +83 -0
- package/src/oauth/account-import/registry.ts +18 -0
- package/src/oauth/account-import/service.ts +75 -0
- package/src/oauth/account-import/types.ts +91 -0
- package/src/oauth/anthropic.ts +12 -1
- package/src/oauth/callback-server.ts +8 -2
- package/src/oauth/chatgpt.ts +12 -1
- package/src/oauth/command-code.ts +239 -0
- package/src/oauth/cursor.ts +46 -5
- package/src/oauth/google-antigravity.ts +35 -3
- package/src/oauth/health.ts +20 -12
- package/src/oauth/index.ts +398 -66
- package/src/oauth/key-providers.ts +16 -0
- package/src/oauth/kimi.ts +16 -2
- package/src/oauth/kiro.ts +50 -6
- package/src/oauth/local-token-detect.ts +11 -2
- package/src/oauth/log.ts +3 -1
- package/src/oauth/login-cli.ts +88 -28
- package/src/oauth/nous.ts +798 -0
- package/src/oauth/store.ts +119 -21
- package/src/oauth/token-guardian.ts +9 -3
- package/src/pi/models.ts +2 -2
- package/src/providers/alibaba-region-migration.ts +1 -1
- package/src/providers/antigravity-models.ts +521 -31
- package/src/providers/base-url-choices.ts +10 -0
- package/src/providers/codex-capacity.ts +292 -0
- package/src/providers/command-code-efforts.ts +144 -0
- package/src/providers/context-cap.ts +22 -5
- package/src/providers/cursor-pool.ts +72 -0
- package/src/providers/derive.ts +253 -6
- package/src/providers/fastwire.ts +501 -0
- package/src/providers/free-directory.ts +10 -7
- package/src/providers/google-vertex-location.ts +14 -0
- package/src/providers/key-failover.ts +71 -3
- package/src/providers/label.ts +1 -1
- package/src/providers/model-discovery-limits.ts +16 -0
- package/src/providers/model-discovery.ts +115 -22
- package/src/providers/model-rename-migration.ts +255 -0
- package/src/providers/model-rename-startup.ts +28 -0
- package/src/providers/openai-sidecar.ts +72 -4
- package/src/providers/openai-tier-startup.ts +31 -2
- package/src/providers/openai-tiers.ts +119 -4
- package/src/providers/openai-virtual-models.ts +1 -0
- package/src/providers/opencode-zen-rate-limit.ts +102 -0
- package/src/providers/provider-id-rewrite.ts +29 -0
- package/src/providers/quota.ts +1319 -38
- package/src/providers/registry.ts +1429 -111
- package/src/providers/request-pacing.ts +310 -0
- package/src/providers/service-tier.ts +277 -0
- package/src/providers/slug-codec.ts +42 -6
- package/src/providers/static-model-discovery.ts +86 -0
- package/src/providers/xai-responses-opt-in.ts +15 -0
- package/src/providers/xai-transport.ts +11 -4
- package/src/reasoning-effort.ts +49 -1
- package/src/responses/compaction.ts +26 -1
- package/src/responses/custom-tool-compat.ts +266 -0
- package/src/responses/hosted-tool-policy.ts +9 -0
- package/src/responses/namespace-tool-compat.ts +355 -0
- package/src/responses/parser.ts +220 -38
- package/src/responses/provider-continuation.ts +98 -0
- package/src/responses/provider-opaque-metadata.ts +73 -0
- package/src/responses/reasoning-envelope.ts +9 -1
- package/src/responses/reasoning-replay-cache.ts +426 -0
- package/src/responses/schema.ts +7 -1
- package/src/responses/spill-store.ts +75 -10
- package/src/responses/state.ts +565 -27
- package/src/responses/thought-signature-replay.ts +347 -0
- package/src/responses/tool-search-compat.ts +301 -0
- package/src/responses/truncated-stop-reason.ts +60 -0
- package/src/router.ts +366 -30
- package/src/routing/analytics.ts +378 -0
- package/src/routing/capability.ts +244 -0
- package/src/routing/compatibility/assemble.ts +73 -0
- package/src/routing/compatibility/behavior.ts +278 -0
- package/src/routing/compatibility/catalog.ts +99 -0
- package/src/routing/compatibility/endpoint.ts +52 -0
- package/src/routing/compatibility/lab-evidence-provider.ts +130 -0
- package/src/routing/compatibility/policy.ts +181 -0
- package/src/routing/compatibility/provider-slot.ts +56 -0
- package/src/routing/compatibility/reader.ts +110 -0
- package/src/routing/compatibility/subject.ts +191 -0
- package/src/routing/compatibility/types.ts +64 -0
- package/src/routing/compatibility/version.ts +104 -0
- package/src/routing/cost.ts +77 -0
- package/src/routing/evaluator.ts +495 -0
- package/src/routing/health.ts +412 -0
- package/src/routing/history/cursor.ts +43 -0
- package/src/routing/history/indexer.ts +605 -0
- package/src/routing/history/schema.ts +72 -0
- package/src/routing/profile-namespace.ts +15 -0
- package/src/routing/profile.ts +547 -0
- package/src/routing/quota.ts +145 -0
- package/src/routing/request-evidence.ts +45 -0
- package/src/routing/trace.ts +776 -0
- package/src/server/adapter-resolve.ts +2 -29
- package/src/server/auth-cors.ts +267 -46
- package/src/server/background-lifecycle.ts +182 -0
- package/src/server/chat-completions.ts +130 -56
- package/src/server/chat-native-sse.ts +331 -0
- package/src/server/chat-native.ts +426 -0
- package/src/server/claude-messages.ts +159 -43
- package/src/server/direct-local-http.ts +347 -0
- package/src/server/effort-policy.ts +18 -0
- package/src/server/github-copilot-responses-repair.ts +338 -0
- package/src/server/gui-static.ts +39 -10
- package/src/server/images.ts +94 -12
- package/src/server/index.ts +865 -181
- package/src/server/lifecycle.ts +284 -13
- package/src/server/live.ts +136 -17
- package/src/server/local-management-read-client.ts +90 -0
- package/src/server/local-provider-reload-client.ts +137 -0
- package/src/server/management/agent-settings-routes.ts +398 -103
- package/src/server/management/api-key-usage.ts +31 -5
- package/src/server/management/body.ts +6 -0
- package/src/server/management/combo-routes.ts +62 -24
- package/src/server/management/config-routes.ts +464 -51
- package/src/server/management/context.ts +80 -2
- package/src/server/management/integration-routes.ts +498 -0
- package/src/server/management/lab-automation-routes.ts +206 -0
- package/src/server/management/lab-routes.ts +563 -0
- package/src/server/management/logs-usage-routes.ts +101 -32
- package/src/server/management/model-routes.ts +189 -131
- package/src/server/management/model-rows.ts +163 -0
- package/src/server/management/native-integration-routes.ts +769 -0
- package/src/server/management/oauth-account-routes.ts +80 -4
- package/src/server/management/provider-capability-config.ts +48 -0
- package/src/server/management/provider-routes.ts +764 -157
- package/src/server/management/request-history-routes.ts +191 -0
- package/src/server/management/routing-analytics-routes.ts +74 -0
- package/src/server/management/routing-profile-routes.ts +380 -0
- package/src/server/management/shared.ts +27 -11
- package/src/server/management/sidebar-routes.ts +47 -31
- package/src/server/management/storage-log-guard-routes.ts +186 -0
- package/src/server/management/sync-response.ts +69 -0
- package/src/server/management/system-restart.ts +288 -32
- package/src/server/management/system-routes.ts +77 -0
- package/src/server/management/usage-summary-cache.ts +9 -1
- package/src/server/management/vision-sidecar-options.ts +167 -0
- package/src/server/management/web-search-sidecar-options.ts +120 -0
- package/src/server/management-api.ts +115 -14
- package/src/server/management-auth.ts +220 -5
- package/src/server/passive-route-linker.ts +66 -0
- package/src/server/ports.ts +41 -1
- package/src/server/proxy-liveness.ts +132 -5
- package/src/server/readiness.ts +99 -0
- package/src/server/relay-eager.ts +82 -42
- package/src/server/relay.ts +236 -76
- package/src/server/request-decompress.ts +113 -6
- package/src/server/request-log.ts +235 -22
- package/src/server/responses/agent-task-recovery-cache.ts +143 -0
- package/src/server/responses/agent-task-recovery.ts +465 -0
- package/src/server/responses/collaboration.ts +204 -35
- package/src/server/responses/compact.ts +442 -55
- package/src/server/responses/core.ts +2872 -331
- package/src/server/responses/empty-completion-guard.ts +276 -0
- package/src/server/responses/encrypted-payload.ts +62 -39
- package/src/server/responses/fetch-helpers.ts +79 -4
- package/src/server/responses/input-admission.ts +185 -0
- package/src/server/responses/pacing-overload.ts +13 -0
- package/src/server/responses/policy-fallback.ts +178 -0
- package/src/server/responses/responses-field-backfill.ts +251 -0
- package/src/server/responses/terminal-guard.ts +26 -5
- package/src/server/responses/upstream-error.ts +5 -0
- package/src/server/responses/ws-upstream.ts +308 -0
- package/src/server/responses-custom-tool-repair.ts +282 -0
- package/src/server/responses-item-id-repair.ts +54 -6
- package/src/server/responses-json-events.ts +90 -0
- package/src/server/responses-model-rewrite.ts +29 -0
- package/src/server/responses-reasoning-summary-rewrite.ts +178 -0
- package/src/server/responses-snapshot-repair.ts +621 -0
- package/src/server/responses-terminal-repair.ts +342 -0
- package/src/server/responses-tool-search-repair.ts +267 -0
- package/src/server/responses-undeclared-tool-guard.ts +153 -0
- package/src/server/responses.ts +18 -2
- package/src/server/search.ts +78 -13
- package/src/server/sse-frame-buffer.ts +292 -0
- package/src/server/sse-payload-rewrite.ts +110 -22
- package/src/server/startup-action-control.ts +8 -1
- package/src/server/startup-health-cache.ts +19 -1
- package/src/server/system-env.ts +80 -9
- package/src/server/ws-bridge.ts +39 -38
- package/src/service-manager-probe.ts +892 -0
- package/src/service.ts +1111 -90
- package/src/sidecar/auth.ts +92 -0
- package/src/sidecar/candidates.ts +83 -0
- package/src/storage/cleanup.ts +2 -2
- package/src/storage/scanner.ts +1 -1
- package/src/storage/worker-lifecycle.ts +14 -14
- package/src/tray/windows-tray.ps1 +83 -9
- package/src/tray/windows.ts +43 -16
- package/src/types/accounts.ts +37 -0
- package/src/types/config.ts +845 -0
- package/src/types/provider.ts +545 -0
- package/src/types/request.ts +384 -0
- package/src/types/tools.ts +131 -0
- package/src/types/wire.ts +80 -0
- package/src/types.ts +104 -1236
- package/src/update/index.ts +32 -19
- package/src/update/job.ts +442 -67
- package/src/update/notify.ts +12 -6
- package/src/update/npm-cache-preflight.d.mts +47 -0
- package/src/update/npm-cache-preflight.mjs +201 -0
- package/src/update/transactional-install.d.mts +22 -0
- package/src/update/transactional-install.mjs +259 -0
- package/src/usage/cost.ts +0 -0
- package/src/usage/expected-prices.ts +268 -16
- package/src/usage/log.ts +606 -41
- package/src/usage/summary.ts +177 -9
- package/src/usage/user-cost-overlay-reconciler.ts +313 -0
- package/src/usage/user-cost-overlays.ts +314 -0
- package/src/vision/anthropic-describe.ts +10 -6
- package/src/vision/backends.ts +97 -0
- package/src/vision/describe.ts +9 -3
- package/src/vision/eligibility.ts +250 -0
- package/src/vision/index.ts +238 -24
- package/src/vision/reasoning.ts +55 -0
- package/src/vision/routed-describe.ts +175 -0
- package/src/vision/timeout-bounds.ts +9 -0
- package/src/web-search/anthropic-executor.ts +13 -7
- package/src/web-search/backends.ts +108 -0
- package/src/web-search/exa-executor.ts +88 -0
- package/src/web-search/executor.ts +11 -3
- package/src/web-search/gemini-executor.ts +141 -0
- package/src/web-search/index.ts +150 -15
- package/src/web-search/loop.ts +279 -50
- package/src/web-search/parse.ts +125 -30
- package/src/web-search/sources.ts +60 -0
- package/src/web-search/xai-executor.ts +219 -0
- package/gui/dist/assets/index-DTpMHS4F.js +0 -67
- package/gui/dist/assets/index-ZNVDE3C7.css +0 -1
- package/gui/dist/provider-icons/antigravity.svg +0 -1
- package/gui/dist/provider-icons/claude.svg +0 -1
- package/gui/dist/provider-icons/copilot.svg +0 -1
- package/gui/dist/provider-icons/cursor.svg +0 -2
- package/gui/dist/provider-icons/gemini.svg +0 -1
- package/gui/dist/provider-icons/grok-color.svg +0 -1
- package/gui/dist/provider-icons/kiro.svg +0 -14
- package/src/cli/internal-dispatch.ts +0 -20
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { readFileSync } from "node:fs";
|
|
3
3
|
import type { CatalogModel } from "../../codex/catalog";
|
|
4
|
-
import { catalogModelSlug, invalidateCodexModelsCache, nativeModelRows, uniqueCatalogModelsForPublicList } from "../../codex/catalog";
|
|
4
|
+
import { catalogModelSlug, invalidateCodexModelsCache, nativeContextLimits, nativeModelRows, uniqueCatalogModelsForPublicList } from "../../codex/catalog";
|
|
5
5
|
import {
|
|
6
6
|
DEFAULT_SUBAGENT_MODELS,
|
|
7
7
|
codexAutoStartEnabled,
|
|
@@ -37,9 +37,14 @@ import { deriveProviderPresets } from "../../providers/derive";
|
|
|
37
37
|
import { providerCodexAccountMode } from "../../providers/registry";
|
|
38
38
|
import { routedSlug, slugEquals } from "../../providers/slug-codec";
|
|
39
39
|
import { clearProviderQuotaCache, fetchProviderQuotaReports } from "../../providers/quota";
|
|
40
|
-
import { isCanonicalOpenAiForwardProvider } from "../../providers/openai-tiers";
|
|
40
|
+
import { isCanonicalOpenAiForwardProvider, OPENAI_CODEX_PROVIDER_ID } from "../../providers/openai-tiers";
|
|
41
41
|
import { clearThreadAccountMap } from "../../codex/routing";
|
|
42
42
|
import { primeCodexPoolQuotas } from "../../codex/auth-api";
|
|
43
|
+
import {
|
|
44
|
+
codexAccountPickerEnabled,
|
|
45
|
+
initializeDefaultCodexAccountNamespaces,
|
|
46
|
+
} from "../../codex/account-namespaces";
|
|
47
|
+
import { catalogRefreshIsPending } from "../../codex/catalog-refresh-status";
|
|
43
48
|
import { DEFAULT_PROVIDER_CONTEXT_CAP, globalContextCapValue, providerContextCap, providerContextCaps, setAllProviderContextCaps, setGlobalContextCapValue, setProviderContextCap } from "../../providers/context-cap";
|
|
44
49
|
import { resolveCodexHomeDir } from "../../codex/home";
|
|
45
50
|
import { readUsageEntries } from "../../usage/log";
|
|
@@ -47,6 +52,31 @@ import { getUsageDebugLogEntries } from "../../usage/debug";
|
|
|
47
52
|
import { parseRange, parseUsageSurface, summarizeUsage } from "../../usage/summary";
|
|
48
53
|
import { stripCodexRuntimeProviderFields } from "../../codex/auth-context";
|
|
49
54
|
import { getProviderRegistryEntry } from "../../providers/registry";
|
|
55
|
+
import { VISION_REASONING_EFFORTS, isVisionReasoningEffort } from "../../reasoning-effort";
|
|
56
|
+
import { normalizeVisionReasoningForModel } from "../../vision/reasoning";
|
|
57
|
+
import {
|
|
58
|
+
findAnthropicVisionProvider,
|
|
59
|
+
isValidVisionTimeoutMs,
|
|
60
|
+
MAX_VISION_TIMEOUT_MS,
|
|
61
|
+
MIN_VISION_TIMEOUT_MS,
|
|
62
|
+
resolveEffectiveVisionModel,
|
|
63
|
+
resolveMaxDescriptionsPerTurn,
|
|
64
|
+
resolveVisionBackend,
|
|
65
|
+
resolveVisionTimeoutMs,
|
|
66
|
+
} from "../../vision";
|
|
67
|
+
import {
|
|
68
|
+
visionCandidateRows,
|
|
69
|
+
visionDescriberIsProvablyBlind,
|
|
70
|
+
visionDescriberRejection,
|
|
71
|
+
visionModelOptionsFor,
|
|
72
|
+
} from "./vision-sidecar-options";
|
|
73
|
+
import {
|
|
74
|
+
webSearchCandidateRows,
|
|
75
|
+
webSearchModelIsRejected,
|
|
76
|
+
webSearchModelOptionsFrom,
|
|
77
|
+
webSearchModelRejection,
|
|
78
|
+
} from "./web-search-sidecar-options";
|
|
79
|
+
import { validateXaiSearchOptions } from "../../web-search/xai-executor";
|
|
50
80
|
import { getDebugLogEntries } from "../../lib/debug-log-buffer";
|
|
51
81
|
import { getInjectionDebugLogEntries } from "../../lib/injection-debug-log";
|
|
52
82
|
import {
|
|
@@ -62,6 +92,7 @@ import { filterRequestLogs, getRequestLogEntries, type RequestLogEntry } from ".
|
|
|
62
92
|
import { estimateComboCost, estimateRequestCost, normalizeCostTokens, tokensPerSecond } from "../../usage/cost";
|
|
63
93
|
import type { PersistedUsageAttempt } from "../../usage/log";
|
|
64
94
|
import { isAllowedRequestOrigin, jsonResponse, providerManagementConfigError, publicProviderBaseUrl, safeConfigDTO } from "../auth-cors";
|
|
95
|
+
import { withProviderServiceTierDTO } from "./provider-capability-config";
|
|
65
96
|
import { applySystemEnvToggle } from "../system-env";
|
|
66
97
|
import { getCachedStartupHealth, invalidateStartupHealthCache } from "../startup-health-cache";
|
|
67
98
|
import { runWindowsTrayAction } from "../windows-tray-control";
|
|
@@ -73,10 +104,147 @@ import type { MetricUnavailableReason, TokPerSecondResult, CostEstimateReason, C
|
|
|
73
104
|
import type { ManagementContext } from "./context";
|
|
74
105
|
import { readManagementJsonBody, rethrowManagementBodyTooLarge } from "./body";
|
|
75
106
|
|
|
107
|
+
async function sidecarVisionResponseSettings(config: OcxConfig): Promise<{
|
|
108
|
+
model: string;
|
|
109
|
+
reasoning: string;
|
|
110
|
+
models: Awaited<ReturnType<typeof visionModelOptionsFor>>;
|
|
111
|
+
}> {
|
|
112
|
+
const vs = config.visionSidecar ?? {};
|
|
113
|
+
// Match the runtime's one selected Anthropic executor for both backend fallback
|
|
114
|
+
// and catalog reachability; resolving it once prevents the two projections drifting.
|
|
115
|
+
const anthropicSidecar = findAnthropicVisionProvider(config);
|
|
116
|
+
// The routed backend reports its own namespaced model verbatim: it is the
|
|
117
|
+
// dispatched value, and collapsing it through the legacy resolver would
|
|
118
|
+
// display a describer the runtime is not using (roadmap 190).
|
|
119
|
+
const routedActive = vs.backend === "routed" && !!vs.model && vs.model.includes("/");
|
|
120
|
+
const backend = routedActive ? "routed" as const : resolveVisionBackend(vs.backend, anthropicSidecar);
|
|
121
|
+
const model = routedActive && vs.model
|
|
122
|
+
? vs.model
|
|
123
|
+
: resolveEffectiveVisionModel(config, backend === "routed" ? resolveVisionBackend(undefined, anthropicSidecar) : backend);
|
|
124
|
+
const reasoning = normalizeVisionReasoningForModel(model, vs.reasoning) ?? "low";
|
|
125
|
+
const models = await visionModelOptionsFor(config, anthropicSidecar);
|
|
126
|
+
// Display-only grandfather: a persisted id stays selectable, but the write gate
|
|
127
|
+
// remains stricter and rejects a model that is positively proven blind.
|
|
128
|
+
if (!models.some(option => option.value === model)) {
|
|
129
|
+
models.unshift({ value: model, label: model, backend });
|
|
130
|
+
}
|
|
131
|
+
return { model, reasoning, models };
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/** One client's outcome from a fan-out sync. Absent from the list means "left alone". */
|
|
135
|
+
interface ClientIntegrationSyncOutcome {
|
|
136
|
+
readonly client: "grok" | "claude-desktop" | "mcode";
|
|
137
|
+
readonly ok: boolean;
|
|
138
|
+
readonly changed?: boolean;
|
|
139
|
+
readonly reason?: string;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Re-inject native clients that are switched ON and file integrations whose
|
|
144
|
+
* OpenCodex ownership record is the operator's durable opt-in.
|
|
145
|
+
*
|
|
146
|
+
* Only Codex used to run here, so a catalog change reached Codex and nothing else: a Grok
|
|
147
|
+
* fence or a written Desktop profile kept the context windows it was created with until the
|
|
148
|
+
* next `ocx start`. The startup path already gates each client on its own toggle
|
|
149
|
+
* (`src/cli/index.ts`), and this is that same fan-out for the on-demand command.
|
|
150
|
+
*
|
|
151
|
+
* A client that is OFF or never connected is omitted from the result rather than reported as skipped — the
|
|
152
|
+
* caller has to be able to tell "not touched" from "tried and failed". A client that fails
|
|
153
|
+
* does not fail the sync: Codex is the one that matters for routing, and a broken Grok file
|
|
154
|
+
* should surface as a warning, not as a 500 on a command that did its main job.
|
|
155
|
+
*/
|
|
156
|
+
async function syncEnabledClientIntegrations(
|
|
157
|
+
port: number | undefined,
|
|
158
|
+
config: OcxConfig,
|
|
159
|
+
): Promise<ClientIntegrationSyncOutcome[]> {
|
|
160
|
+
if (port === undefined) return [];
|
|
161
|
+
const { claudeDesktopIntegrationEnabled, grokIntegrationEnabled } = await import("../../codex/desired-state");
|
|
162
|
+
const out: ClientIntegrationSyncOutcome[] = [];
|
|
163
|
+
|
|
164
|
+
if (grokIntegrationEnabled(config)) {
|
|
165
|
+
try {
|
|
166
|
+
const { syncGrokConfig } = await import("../../grok/sync");
|
|
167
|
+
const r = await syncGrokConfig(port, config, config.hostname ? { hostname: config.hostname } : {});
|
|
168
|
+
out.push(r.ok
|
|
169
|
+
? { client: "grok", ok: true, changed: r.changed === true }
|
|
170
|
+
: { client: "grok", ok: false, reason: r.message });
|
|
171
|
+
} catch (error) {
|
|
172
|
+
out.push({ client: "grok", ok: false, reason: error instanceof Error ? error.message : String(error) });
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (claudeDesktopIntegrationEnabled(config)) {
|
|
177
|
+
try {
|
|
178
|
+
const { writeDesktop3pConfig } = await import("../../claude/desktop-3p");
|
|
179
|
+
const { desktopVisibleNativeSlugs, filterCatalogVisibleModels } = await import("../../codex/catalog");
|
|
180
|
+
const { fetchAllModels } = await import("../management-api");
|
|
181
|
+
const routed = filterCatalogVisibleModels(await fetchAllModels(config), config)
|
|
182
|
+
.map(model => ({ provider: model.provider, id: model.id, contextWindow: model.contextWindow }));
|
|
183
|
+
const r = writeDesktop3pConfig(
|
|
184
|
+
port,
|
|
185
|
+
[...desktopVisibleNativeSlugs(config)],
|
|
186
|
+
routed,
|
|
187
|
+
config.apiKeys?.[0]?.key,
|
|
188
|
+
"static",
|
|
189
|
+
config.claudeCode?.desktopProfile,
|
|
190
|
+
nativeContextLimits(config),
|
|
191
|
+
);
|
|
192
|
+
out.push(r.written
|
|
193
|
+
? { client: "claude-desktop", ok: true, changed: true }
|
|
194
|
+
: { client: "claude-desktop", ok: false, reason: r.reason ?? "Claude Desktop write failed" });
|
|
195
|
+
} catch (error) {
|
|
196
|
+
out.push({ client: "claude-desktop", ok: false, reason: error instanceof Error ? error.message : String(error) });
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
try {
|
|
201
|
+
const { refreshOwnedIntegration } = await import("../../integrations/owned-refresh");
|
|
202
|
+
const result = await refreshOwnedIntegration({
|
|
203
|
+
clientId: "mcode",
|
|
204
|
+
models: async () => {
|
|
205
|
+
const { loadExportModels } = await import("./model-rows");
|
|
206
|
+
return loadExportModels(config);
|
|
207
|
+
},
|
|
208
|
+
config,
|
|
209
|
+
port,
|
|
210
|
+
});
|
|
211
|
+
if (result) {
|
|
212
|
+
out.push(result.ok
|
|
213
|
+
? {
|
|
214
|
+
client: "mcode",
|
|
215
|
+
ok: true,
|
|
216
|
+
changed: result.changed === true,
|
|
217
|
+
...(result.reason ? { reason: result.reason } : {}),
|
|
218
|
+
}
|
|
219
|
+
: { client: "mcode", ok: false, reason: result.reason });
|
|
220
|
+
}
|
|
221
|
+
} catch (error) {
|
|
222
|
+
out.push({ client: "mcode", ok: false, reason: error instanceof Error ? error.message : String(error) });
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return out;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function publicVisionSidecarSettings(
|
|
229
|
+
config: OcxConfig,
|
|
230
|
+
vision: Awaited<ReturnType<typeof sidecarVisionResponseSettings>>,
|
|
231
|
+
) {
|
|
232
|
+
const vs = config.visionSidecar ?? {};
|
|
233
|
+
return {
|
|
234
|
+
enabled: vs.enabled !== false,
|
|
235
|
+
model: vision.model,
|
|
236
|
+
backend: vs.backend,
|
|
237
|
+
reasoning: vision.reasoning,
|
|
238
|
+
maxDescriptionsPerTurn: resolveMaxDescriptionsPerTurn(vs.maxDescriptionsPerTurn),
|
|
239
|
+
timeoutMs: resolveVisionTimeoutMs(vs.timeoutMs),
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
|
|
76
243
|
export async function handleConfigRoutes(ctx: ManagementContext): Promise<Response | null> {
|
|
77
|
-
const { req, url, config, deps,
|
|
244
|
+
const { req, url, config, deps, convergeCodexCatalog, syncClaudeAgentDefsBestEffort } = ctx;
|
|
245
|
+
const readStartupHealth = deps.getCachedStartupHealth ?? getCachedStartupHealth;
|
|
78
246
|
if (url.pathname === "/api/config" && req.method === "GET") {
|
|
79
|
-
return jsonResponse(safeConfigDTO(config));
|
|
247
|
+
return jsonResponse(withProviderServiceTierDTO(safeConfigDTO(config), config));
|
|
80
248
|
}
|
|
81
249
|
|
|
82
250
|
if (url.pathname === "/api/config" && req.method === "PUT") {
|
|
@@ -129,7 +297,8 @@ export async function handleConfigRoutes(ctx: ManagementContext): Promise<Respon
|
|
|
129
297
|
hostname: config.hostname ?? "127.0.0.1",
|
|
130
298
|
streamMode: config.streamMode ?? "auto",
|
|
131
299
|
appOwnedMemoryBudgetMb: config.appOwnedMemoryBudgetMb ?? 256,
|
|
132
|
-
|
|
300
|
+
codexAccountPickerEnabled: codexAccountPickerEnabled(config),
|
|
301
|
+
startupHealth: await readStartupHealth(config),
|
|
133
302
|
codexRuntime: {
|
|
134
303
|
path: displayCodexRuntimePath(resolved.runtime.command),
|
|
135
304
|
version: resolved.runtime.version,
|
|
@@ -151,7 +320,7 @@ export async function handleConfigRoutes(ctx: ManagementContext): Promise<Respon
|
|
|
151
320
|
}
|
|
152
321
|
|
|
153
322
|
if (url.pathname === "/api/startup-health" && req.method === "GET") {
|
|
154
|
-
return jsonResponse(await
|
|
323
|
+
return jsonResponse(await readStartupHealth(config));
|
|
155
324
|
}
|
|
156
325
|
|
|
157
326
|
if (url.pathname === "/api/startup-action" && req.method === "POST") {
|
|
@@ -205,10 +374,20 @@ export async function handleConfigRoutes(ctx: ManagementContext): Promise<Respon
|
|
|
205
374
|
// (a Windows service does not inherit shell env). A stream-shape
|
|
206
375
|
// change applies to NEW turns only — the config object is shared by
|
|
207
376
|
// reference with the request handlers, no restart needed.
|
|
208
|
-
let
|
|
209
|
-
try {
|
|
210
|
-
if (
|
|
211
|
-
|
|
377
|
+
let parsedBody: unknown;
|
|
378
|
+
try { parsedBody = await readManagementJsonBody(req); } catch (error) { rethrowManagementBodyTooLarge(error); return jsonResponse({ error: "invalid JSON body" }, 400); }
|
|
379
|
+
if (!isPlainRecord(parsedBody)) return jsonResponse({ error: "settings body must be an object" }, 400);
|
|
380
|
+
const body = parsedBody as {
|
|
381
|
+
codexAutoStart?: unknown;
|
|
382
|
+
streamMode?: unknown;
|
|
383
|
+
appOwnedMemoryBudgetMb?: unknown;
|
|
384
|
+
codexAccountPickerEnabled?: unknown;
|
|
385
|
+
};
|
|
386
|
+
if (body.codexAutoStart === undefined
|
|
387
|
+
&& body.streamMode === undefined
|
|
388
|
+
&& body.appOwnedMemoryBudgetMb === undefined
|
|
389
|
+
&& body.codexAccountPickerEnabled === undefined) {
|
|
390
|
+
return jsonResponse({ error: "provide codexAutoStart, streamMode, appOwnedMemoryBudgetMb, or codexAccountPickerEnabled" }, 400);
|
|
212
391
|
}
|
|
213
392
|
if (body.codexAutoStart !== undefined && typeof body.codexAutoStart !== "boolean") {
|
|
214
393
|
return jsonResponse({ error: "codexAutoStart boolean is required" }, 400);
|
|
@@ -216,6 +395,10 @@ export async function handleConfigRoutes(ctx: ManagementContext): Promise<Respon
|
|
|
216
395
|
if (body.streamMode !== undefined && !isStreamMode(body.streamMode)) {
|
|
217
396
|
return jsonResponse({ error: "streamMode must be auto, legacy-tee, or eager-relay" }, 400);
|
|
218
397
|
}
|
|
398
|
+
if (body.codexAccountPickerEnabled !== undefined
|
|
399
|
+
&& typeof body.codexAccountPickerEnabled !== "boolean") {
|
|
400
|
+
return jsonResponse({ error: "codexAccountPickerEnabled boolean is required" }, 400);
|
|
401
|
+
}
|
|
219
402
|
if (body.appOwnedMemoryBudgetMb !== undefined && (
|
|
220
403
|
typeof body.appOwnedMemoryBudgetMb !== "number"
|
|
221
404
|
|| !Number.isInteger(body.appOwnedMemoryBudgetMb)
|
|
@@ -224,31 +407,77 @@ export async function handleConfigRoutes(ctx: ManagementContext): Promise<Respon
|
|
|
224
407
|
)) {
|
|
225
408
|
return jsonResponse({ error: `appOwnedMemoryBudgetMb must be an integer from ${MIN_APP_OWNED_MEMORY_BUDGET_MB} to ${MAX_APP_OWNED_MEMORY_BUDGET_MB}` }, 400);
|
|
226
409
|
}
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
410
|
+
const previousSettings = {
|
|
411
|
+
codexAutoStart: config.codexAutoStart,
|
|
412
|
+
hasCodexAutoStart: Object.hasOwn(config, "codexAutoStart"),
|
|
413
|
+
streamMode: config.streamMode,
|
|
414
|
+
hasStreamMode: Object.hasOwn(config, "streamMode"),
|
|
415
|
+
appOwnedMemoryBudgetMb: config.appOwnedMemoryBudgetMb,
|
|
416
|
+
hasAppOwnedMemoryBudgetMb: Object.hasOwn(config, "appOwnedMemoryBudgetMb"),
|
|
417
|
+
codexAccountNamespaces: config.codexAccountNamespaces,
|
|
418
|
+
hasCodexAccountNamespaces: Object.hasOwn(config, "codexAccountNamespaces"),
|
|
419
|
+
codexAccountPickerEnabled: config.codexAccountPickerEnabled,
|
|
420
|
+
hasCodexAccountPickerEnabled: Object.hasOwn(config, "codexAccountPickerEnabled"),
|
|
421
|
+
};
|
|
422
|
+
const pickerWasEnabled = codexAccountPickerEnabled(config);
|
|
423
|
+
let pickerIsEnabled = pickerWasEnabled;
|
|
424
|
+
try {
|
|
425
|
+
if (typeof body.codexAutoStart === "boolean") {
|
|
426
|
+
config.codexAutoStart = body.codexAutoStart;
|
|
235
427
|
}
|
|
428
|
+
if (body.streamMode !== undefined) {
|
|
429
|
+
if (body.streamMode === "auto") {
|
|
430
|
+
delete config.streamMode;
|
|
431
|
+
} else {
|
|
432
|
+
config.streamMode = body.streamMode as "legacy-tee" | "eager-relay";
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
if (typeof body.appOwnedMemoryBudgetMb === "number") {
|
|
436
|
+
config.appOwnedMemoryBudgetMb = body.appOwnedMemoryBudgetMb;
|
|
437
|
+
}
|
|
438
|
+
if (body.codexAccountPickerEnabled === true) {
|
|
439
|
+
config.codexAccountPickerEnabled = true;
|
|
440
|
+
initializeDefaultCodexAccountNamespaces(config);
|
|
441
|
+
} else if (body.codexAccountPickerEnabled === false) {
|
|
442
|
+
config.codexAccountPickerEnabled = false;
|
|
443
|
+
}
|
|
444
|
+
pickerIsEnabled = codexAccountPickerEnabled(config);
|
|
445
|
+
(deps.saveConfigPreservingClaudeCode ?? saveConfigPreservingClaudeCode)(config);
|
|
446
|
+
} catch (error) {
|
|
447
|
+
if (previousSettings.hasCodexAutoStart) config.codexAutoStart = previousSettings.codexAutoStart;
|
|
448
|
+
else delete config.codexAutoStart;
|
|
449
|
+
if (previousSettings.hasStreamMode) config.streamMode = previousSettings.streamMode;
|
|
450
|
+
else delete config.streamMode;
|
|
451
|
+
if (previousSettings.hasAppOwnedMemoryBudgetMb) {
|
|
452
|
+
config.appOwnedMemoryBudgetMb = previousSettings.appOwnedMemoryBudgetMb;
|
|
453
|
+
} else delete config.appOwnedMemoryBudgetMb;
|
|
454
|
+
if (previousSettings.hasCodexAccountNamespaces) {
|
|
455
|
+
config.codexAccountNamespaces = previousSettings.codexAccountNamespaces;
|
|
456
|
+
} else delete config.codexAccountNamespaces;
|
|
457
|
+
if (previousSettings.hasCodexAccountPickerEnabled) {
|
|
458
|
+
config.codexAccountPickerEnabled = previousSettings.codexAccountPickerEnabled;
|
|
459
|
+
} else delete config.codexAccountPickerEnabled;
|
|
460
|
+
throw error;
|
|
236
461
|
}
|
|
237
|
-
if (typeof body.appOwnedMemoryBudgetMb === "number") {
|
|
238
|
-
config.appOwnedMemoryBudgetMb = body.appOwnedMemoryBudgetMb;
|
|
239
|
-
}
|
|
240
|
-
saveConfigPreservingClaudeCode(config);
|
|
241
462
|
if (typeof body.appOwnedMemoryBudgetMb === "number") {
|
|
242
463
|
configureAppOwnedMemoryBudget(resolveAppOwnedMemoryBudgetBytes(body.appOwnedMemoryBudgetMb));
|
|
243
464
|
enforceAppOwnedMemoryBudget();
|
|
244
465
|
}
|
|
466
|
+
const catalogRefresh = pickerWasEnabled !== pickerIsEnabled
|
|
467
|
+
? await convergeCodexCatalog()
|
|
468
|
+
: undefined;
|
|
469
|
+
const catalogRefreshPending = catalogRefresh
|
|
470
|
+
? catalogRefreshIsPending(catalogRefresh)
|
|
471
|
+
: false;
|
|
245
472
|
invalidateStartupHealthCache();
|
|
246
473
|
return jsonResponse({
|
|
247
474
|
ok: true,
|
|
248
475
|
codexAutoStart: codexAutoStartEnabled(config),
|
|
249
476
|
streamMode: config.streamMode ?? "auto",
|
|
250
477
|
appOwnedMemoryBudgetMb: config.appOwnedMemoryBudgetMb ?? 256,
|
|
251
|
-
|
|
478
|
+
codexAccountPickerEnabled: pickerIsEnabled,
|
|
479
|
+
catalogRefreshPending,
|
|
480
|
+
startupHealth: await readStartupHealth(config),
|
|
252
481
|
});
|
|
253
482
|
}
|
|
254
483
|
|
|
@@ -261,11 +490,25 @@ export async function handleConfigRoutes(ctx: ManagementContext): Promise<Respon
|
|
|
261
490
|
if (url.pathname === "/api/sync" && req.method === "POST") {
|
|
262
491
|
const { syncModelsToCodex } = await import("../../codex/sync");
|
|
263
492
|
const { attachStaleAppServerHint } = await import("../../codex/app-server-processes");
|
|
264
|
-
const
|
|
493
|
+
const { readRuntimePort, loadConfig } = await import("../../config");
|
|
494
|
+
// Never use the server-captured startup object for a durable integration
|
|
495
|
+
// decision. A toggle may have persisted while this process was gathering.
|
|
496
|
+
const runtime = readRuntimePort(process.pid);
|
|
497
|
+
const config = loadConfig();
|
|
498
|
+
const result = await syncModelsToCodex(runtime?.port, config, null);
|
|
499
|
+
// A sync used to stop here, so a Grok fence or a Desktop profile kept whatever
|
|
500
|
+
// context windows it was written with while the Codex catalog moved on. The
|
|
501
|
+
// startup path already fans out to every enabled client; this is the same fan-out
|
|
502
|
+
// for the on-demand command. Codex goes first because the others read its catalog.
|
|
503
|
+
const integrations = result.status === "refused"
|
|
504
|
+
? []
|
|
505
|
+
: await syncEnabledClientIntegrations(runtime?.port, config);
|
|
506
|
+
const status = result.status === "refused" ? 409 : (result.status === "skipped" || result.ok ? 200 : 500);
|
|
265
507
|
return jsonResponse({
|
|
266
508
|
...attachStaleAppServerHint(result),
|
|
509
|
+
...(integrations.length > 0 ? { integrations } : {}),
|
|
267
510
|
...(result.ok ? {} : { error: result.message }),
|
|
268
|
-
},
|
|
511
|
+
}, status);
|
|
269
512
|
}
|
|
270
513
|
|
|
271
514
|
if (url.pathname === "/api/update/check" && req.method === "GET") {
|
|
@@ -306,14 +549,20 @@ export async function handleConfigRoutes(ctx: ManagementContext): Promise<Respon
|
|
|
306
549
|
|
|
307
550
|
if (url.pathname === "/api/sidecar-settings" && req.method === "GET") {
|
|
308
551
|
const ws = config.webSearchSidecar ?? {};
|
|
309
|
-
const
|
|
552
|
+
const vision = await sidecarVisionResponseSettings(config);
|
|
553
|
+
const webSearchCandidates = await webSearchCandidateRows(config);
|
|
310
554
|
return jsonResponse({
|
|
311
|
-
webSearch: {
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
555
|
+
webSearch: {
|
|
556
|
+
model: ws.model ?? "gpt-5.6-luna",
|
|
557
|
+
backend: ws.backend,
|
|
558
|
+
streamRoutedModelOutput: ws.streamRoutedModelOutput === true,
|
|
559
|
+
...(ws.xSearch ? { xSearch: ws.xSearch } : {}),
|
|
316
560
|
},
|
|
561
|
+
vision: publicVisionSidecarSettings(config, vision),
|
|
562
|
+
visionModels: vision.models,
|
|
563
|
+
// ALWAYS present: the dashboard treats an omitted list as "no filter" and
|
|
564
|
+
// falls back to the full model union, so empty must be [] (review B3).
|
|
565
|
+
webSearchModels: webSearchModelOptionsFrom(config, webSearchCandidates),
|
|
317
566
|
});
|
|
318
567
|
}
|
|
319
568
|
|
|
@@ -326,16 +575,32 @@ export async function handleConfigRoutes(ctx: ManagementContext): Promise<Respon
|
|
|
326
575
|
if (raw.webSearch !== undefined && !isPlainRecord(raw.webSearch)) return jsonResponse({ error: "webSearch must be an object" }, 400);
|
|
327
576
|
if (raw.vision !== undefined && !isPlainRecord(raw.vision)) return jsonResponse({ error: "vision must be an object" }, 400);
|
|
328
577
|
const body = raw as {
|
|
329
|
-
webSearch?: { model?: unknown; backend?: unknown; reasoning?: unknown };
|
|
330
|
-
vision?: {
|
|
578
|
+
webSearch?: { model?: unknown; backend?: unknown; reasoning?: unknown; streamRoutedModelOutput?: unknown; exaApiKey?: unknown; xSearch?: unknown };
|
|
579
|
+
vision?: {
|
|
580
|
+
model?: unknown;
|
|
581
|
+
backend?: unknown;
|
|
582
|
+
reasoning?: unknown;
|
|
583
|
+
maxDescriptionsPerTurn?: unknown;
|
|
584
|
+
enabled?: unknown;
|
|
585
|
+
timeoutMs?: unknown;
|
|
586
|
+
};
|
|
331
587
|
};
|
|
588
|
+
const WEB_SEARCH_BACKENDS_UNION = ["openai", "anthropic", "xai", "gemini", "exa"] as const;
|
|
332
589
|
if (body.webSearch && body.webSearch.backend !== undefined && body.webSearch.backend !== null
|
|
333
|
-
&&
|
|
334
|
-
return jsonResponse({ error: "webSearch.backend must be openai, anthropic, or null" }, 400);
|
|
590
|
+
&& !WEB_SEARCH_BACKENDS_UNION.includes(body.webSearch.backend as never)) {
|
|
591
|
+
return jsonResponse({ error: "webSearch.backend must be openai, anthropic, xai, gemini, exa, or null" }, 400);
|
|
592
|
+
}
|
|
593
|
+
if (body.webSearch?.model !== undefined && typeof body.webSearch.model !== "string") {
|
|
594
|
+
return jsonResponse({ error: "webSearch.model must be a string" }, 400);
|
|
595
|
+
}
|
|
596
|
+
if (body.webSearch && body.webSearch.streamRoutedModelOutput !== undefined
|
|
597
|
+
&& typeof body.webSearch.streamRoutedModelOutput !== "boolean") {
|
|
598
|
+
return jsonResponse({ error: "webSearch.streamRoutedModelOutput must be a boolean" }, 400);
|
|
335
599
|
}
|
|
336
600
|
if (body.vision && body.vision.backend !== undefined
|
|
337
|
-
&& body.vision.backend !== null && body.vision.backend !== "openai" && body.vision.backend !== "anthropic"
|
|
338
|
-
|
|
601
|
+
&& body.vision.backend !== null && body.vision.backend !== "openai" && body.vision.backend !== "anthropic"
|
|
602
|
+
&& body.vision.backend !== "routed") {
|
|
603
|
+
return jsonResponse({ error: "vision.backend must be openai, anthropic, routed, or null" }, 400);
|
|
339
604
|
}
|
|
340
605
|
if (body.vision && body.vision.maxDescriptionsPerTurn !== undefined
|
|
341
606
|
&& (typeof body.vision.maxDescriptionsPerTurn !== "number"
|
|
@@ -343,17 +608,145 @@ export async function handleConfigRoutes(ctx: ManagementContext): Promise<Respon
|
|
|
343
608
|
|| body.vision.maxDescriptionsPerTurn <= 0)) {
|
|
344
609
|
return jsonResponse({ error: "vision.maxDescriptionsPerTurn must be a positive integer" }, 400);
|
|
345
610
|
}
|
|
611
|
+
if (body.vision && body.vision.enabled !== undefined && typeof body.vision.enabled !== "boolean") {
|
|
612
|
+
return jsonResponse({ error: "vision.enabled must be a boolean" }, 400);
|
|
613
|
+
}
|
|
614
|
+
if (body.vision && body.vision.timeoutMs !== undefined && !isValidVisionTimeoutMs(body.vision.timeoutMs)) {
|
|
615
|
+
return jsonResponse({
|
|
616
|
+
error: `vision.timeoutMs must be an integer from ${MIN_VISION_TIMEOUT_MS} to ${MAX_VISION_TIMEOUT_MS}`,
|
|
617
|
+
}, 400);
|
|
618
|
+
}
|
|
619
|
+
if (body.vision?.reasoning !== undefined && !isVisionReasoningEffort(body.vision.reasoning)) {
|
|
620
|
+
return jsonResponse({ error: `vision.reasoning must be ${VISION_REASONING_EFFORTS.join(", ")}` }, 400);
|
|
621
|
+
}
|
|
622
|
+
// Reject ONLY a model we can prove is blind. An id nothing knows about stays
|
|
623
|
+
// allowed: the operator may be ahead of our catalog, and the runtime never
|
|
624
|
+
// required catalog membership (`tests/vision-reasoning-contract.test.ts`
|
|
625
|
+
// pins `custom-vision` → 200). The catalog is read ONCE and reused for the
|
|
626
|
+
// rejection body, so a 400 cannot cost two provider fetches.
|
|
627
|
+
if (body.vision && typeof body.vision.model === "string" && body.vision.model !== "") {
|
|
628
|
+
const requested = body.vision.model;
|
|
629
|
+
const candidates = await visionCandidateRows(config);
|
|
630
|
+
const hint = body.vision.backend === "anthropic" || body.vision.backend === "openai"
|
|
631
|
+
|| body.vision.backend === "routed"
|
|
632
|
+
? body.vision.backend
|
|
633
|
+
: config.visionSidecar?.backend;
|
|
634
|
+
// Coherence (roadmap 170 r2): the forward/OAuth executors POST the model
|
|
635
|
+
// string VERBATIM, so a namespaced id on those backends persists a wire
|
|
636
|
+
// id they cannot run; and "routed" without a namespace cannot route.
|
|
637
|
+
const effectiveBackend = hint ?? "openai";
|
|
638
|
+
const namespaced = requested.includes("/");
|
|
639
|
+
if (namespaced && effectiveBackend !== "routed") {
|
|
640
|
+
return jsonResponse({ error: `vision.model "${requested}" is provider-namespaced; it requires vision.backend "routed"` }, 400);
|
|
641
|
+
}
|
|
642
|
+
if (!namespaced && effectiveBackend === "routed") {
|
|
643
|
+
return jsonResponse({ error: `vision.backend "routed" requires a provider-namespaced vision.model ("provider/model"); got "${requested}"` }, 400);
|
|
644
|
+
}
|
|
645
|
+
if (visionDescriberIsProvablyBlind(config, requested, candidates, hint)) {
|
|
646
|
+
return jsonResponse(visionDescriberRejection("vision.model", requested, config, candidates), 400);
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
let normalizedVisionReasoning: ReturnType<typeof normalizeVisionReasoningForModel>;
|
|
651
|
+
let visionReasoningTouched = false;
|
|
652
|
+
if (body.vision && (body.vision.model !== undefined || body.vision.reasoning !== undefined)) {
|
|
653
|
+
visionReasoningTouched = true;
|
|
654
|
+
const model = typeof body.vision.model === "string"
|
|
655
|
+
? (body.vision.model === "" ? "gpt-5.4-mini" : body.vision.model)
|
|
656
|
+
: (config.visionSidecar?.model || "gpt-5.4-mini");
|
|
657
|
+
const sourceReasoning = body.vision.reasoning ?? config.visionSidecar?.reasoning;
|
|
658
|
+
normalizedVisionReasoning = sourceReasoning === undefined
|
|
659
|
+
? undefined
|
|
660
|
+
: normalizeVisionReasoningForModel(model, sourceReasoning);
|
|
661
|
+
}
|
|
662
|
+
|
|
346
663
|
if (body.webSearch) {
|
|
347
|
-
|
|
664
|
+
const pairTouched = body.webSearch.model !== undefined || body.webSearch.backend !== undefined;
|
|
665
|
+
const effectiveBackend = body.webSearch.backend === "anthropic"
|
|
666
|
+
? "anthropic"
|
|
667
|
+
: body.webSearch.backend === "openai" || body.webSearch.backend === null
|
|
668
|
+
? "openai"
|
|
669
|
+
: config.webSearchSidecar?.backend ?? "openai";
|
|
670
|
+
const effectiveModel = typeof body.webSearch.model === "string"
|
|
671
|
+
? body.webSearch.model || undefined
|
|
672
|
+
: config.webSearchSidecar?.model;
|
|
673
|
+
if (pairTouched && effectiveModel) {
|
|
674
|
+
const candidates = await webSearchCandidateRows(config);
|
|
675
|
+
if (webSearchModelIsRejected(effectiveBackend, effectiveModel, candidates)) {
|
|
676
|
+
return jsonResponse(webSearchModelRejection("webSearch.model", effectiveBackend, effectiveModel, candidates), 400);
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
const webSearchCandidate = { ...config.webSearchSidecar };
|
|
348
680
|
if (typeof body.webSearch.model === "string") {
|
|
349
|
-
if (body.webSearch.model === "") delete
|
|
350
|
-
else
|
|
681
|
+
if (body.webSearch.model === "") delete webSearchCandidate.model;
|
|
682
|
+
else webSearchCandidate.model = body.webSearch.model;
|
|
351
683
|
}
|
|
352
|
-
if (body.webSearch.backend === null) delete
|
|
353
|
-
else if (
|
|
354
|
-
|
|
684
|
+
if (body.webSearch.backend === null) delete webSearchCandidate.backend;
|
|
685
|
+
else if (WEB_SEARCH_BACKENDS_UNION.includes(body.webSearch.backend as never)) {
|
|
686
|
+
webSearchCandidate.backend = body.webSearch.backend as typeof WEB_SEARCH_BACKENDS_UNION[number];
|
|
355
687
|
}
|
|
356
|
-
if (typeof body.webSearch.reasoning === "string")
|
|
688
|
+
if (typeof body.webSearch.reasoning === "string") webSearchCandidate.reasoning = body.webSearch.reasoning;
|
|
689
|
+
// Operator secret for the exa backend: string sets, empty string clears. The GET
|
|
690
|
+
// payload deliberately never carries it and redact.ts strips the key from logs.
|
|
691
|
+
if (typeof body.webSearch.exaApiKey === "string") {
|
|
692
|
+
if (body.webSearch.exaApiKey === "") delete webSearchCandidate.exaApiKey;
|
|
693
|
+
else webSearchCandidate.exaApiKey = body.webSearch.exaApiKey;
|
|
694
|
+
}
|
|
695
|
+
// Opt-in x_search block (L7): null clears; an object is doc-validated before persisting.
|
|
696
|
+
if (body.webSearch.xSearch === null) delete webSearchCandidate.xSearch;
|
|
697
|
+
else if (body.webSearch.xSearch !== undefined) {
|
|
698
|
+
if (!isPlainRecord(body.webSearch.xSearch)) {
|
|
699
|
+
return jsonResponse({ error: "webSearch.xSearch must be an object or null" }, 400);
|
|
700
|
+
}
|
|
701
|
+
const x = body.webSearch.xSearch as Record<string, unknown>;
|
|
702
|
+
const allowedXSearchKeys = new Set([
|
|
703
|
+
"enabled",
|
|
704
|
+
"allowedXHandles",
|
|
705
|
+
"excludedXHandles",
|
|
706
|
+
"fromDate",
|
|
707
|
+
"toDate",
|
|
708
|
+
]);
|
|
709
|
+
const unknownKey = Object.keys(x).find(key => !allowedXSearchKeys.has(key));
|
|
710
|
+
if (unknownKey !== undefined) {
|
|
711
|
+
return jsonResponse({ error: `webSearch.xSearch.${unknownKey} is not a supported field` }, 400);
|
|
712
|
+
}
|
|
713
|
+
if (x.enabled !== undefined && typeof x.enabled !== "boolean") {
|
|
714
|
+
return jsonResponse({ error: "webSearch.xSearch.enabled must be a boolean" }, 400);
|
|
715
|
+
}
|
|
716
|
+
for (const field of ["allowedXHandles", "excludedXHandles"] as const) {
|
|
717
|
+
const value = x[field];
|
|
718
|
+
if (value !== undefined && (!Array.isArray(value) || !value.every(handle => typeof handle === "string"))) {
|
|
719
|
+
return jsonResponse({ error: `webSearch.xSearch.${field} must be an array of strings` }, 400);
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
for (const field of ["fromDate", "toDate"] as const) {
|
|
723
|
+
if (x[field] !== undefined && typeof x[field] !== "string") {
|
|
724
|
+
return jsonResponse({ error: `webSearch.xSearch.${field} must be an ISO-8601 date (YYYY-MM-DD)` }, 400);
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
const candidate = {
|
|
728
|
+
...(x.enabled === true ? { enabled: true } : {}),
|
|
729
|
+
...(x.allowedXHandles !== undefined ? { allowedXHandles: x.allowedXHandles as string[] } : {}),
|
|
730
|
+
...(x.excludedXHandles !== undefined ? { excludedXHandles: x.excludedXHandles as string[] } : {}),
|
|
731
|
+
...(x.fromDate !== undefined ? { fromDate: x.fromDate as string } : {}),
|
|
732
|
+
...(x.toDate !== undefined ? { toDate: x.toDate as string } : {}),
|
|
733
|
+
};
|
|
734
|
+
const invalid = validateXaiSearchOptions({
|
|
735
|
+
xSearch: candidate.enabled,
|
|
736
|
+
allowedXHandles: candidate.allowedXHandles,
|
|
737
|
+
excludedXHandles: candidate.excludedXHandles,
|
|
738
|
+
fromDate: candidate.fromDate,
|
|
739
|
+
toDate: candidate.toDate,
|
|
740
|
+
});
|
|
741
|
+
if (invalid) return jsonResponse({ error: `webSearch.xSearch invalid: ${invalid}` }, 400);
|
|
742
|
+
webSearchCandidate.xSearch = candidate;
|
|
743
|
+
}
|
|
744
|
+
if (typeof body.webSearch.streamRoutedModelOutput === "boolean") {
|
|
745
|
+
// `false` is the default — drop the key so config files stay minimal.
|
|
746
|
+
if (body.webSearch.streamRoutedModelOutput) webSearchCandidate.streamRoutedModelOutput = true;
|
|
747
|
+
else delete webSearchCandidate.streamRoutedModelOutput;
|
|
748
|
+
}
|
|
749
|
+
config.webSearchSidecar = webSearchCandidate;
|
|
357
750
|
}
|
|
358
751
|
if (body.vision) {
|
|
359
752
|
config.visionSidecar = { ...config.visionSidecar };
|
|
@@ -362,24 +755,44 @@ export async function handleConfigRoutes(ctx: ManagementContext): Promise<Respon
|
|
|
362
755
|
else config.visionSidecar.model = body.vision.model;
|
|
363
756
|
}
|
|
364
757
|
if (body.vision.backend === null) delete config.visionSidecar.backend;
|
|
365
|
-
else if (body.vision.backend === "openai" || body.vision.backend === "anthropic"
|
|
758
|
+
else if (body.vision.backend === "openai" || body.vision.backend === "anthropic"
|
|
759
|
+
|| body.vision.backend === "routed") {
|
|
366
760
|
config.visionSidecar.backend = body.vision.backend;
|
|
367
761
|
}
|
|
368
762
|
if (typeof body.vision.maxDescriptionsPerTurn === "number") {
|
|
369
763
|
config.visionSidecar.maxDescriptionsPerTurn = body.vision.maxDescriptionsPerTurn;
|
|
370
764
|
}
|
|
765
|
+
if (typeof body.vision.enabled === "boolean") {
|
|
766
|
+
// `true` is the default — drop the key so disable/re-enable does not rewrite the file.
|
|
767
|
+
if (body.vision.enabled) delete config.visionSidecar.enabled;
|
|
768
|
+
else config.visionSidecar.enabled = false;
|
|
769
|
+
}
|
|
770
|
+
if (typeof body.vision.timeoutMs === "number") {
|
|
771
|
+
config.visionSidecar.timeoutMs = body.vision.timeoutMs;
|
|
772
|
+
}
|
|
773
|
+
if (visionReasoningTouched) {
|
|
774
|
+
if (normalizedVisionReasoning === undefined) delete config.visionSidecar.reasoning;
|
|
775
|
+
else config.visionSidecar.reasoning = normalizedVisionReasoning;
|
|
776
|
+
}
|
|
371
777
|
}
|
|
372
778
|
saveConfigPreservingClaudeCode(config);
|
|
373
779
|
const ws = config.webSearchSidecar ?? {};
|
|
374
|
-
const
|
|
780
|
+
const vision = await sidecarVisionResponseSettings(config);
|
|
781
|
+
const savedWebSearchCandidates = await webSearchCandidateRows(config);
|
|
375
782
|
return jsonResponse({
|
|
376
783
|
ok: true,
|
|
377
|
-
webSearch: {
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
784
|
+
webSearch: {
|
|
785
|
+
model: ws.model ?? "gpt-5.6-luna",
|
|
786
|
+
backend: ws.backend,
|
|
787
|
+
streamRoutedModelOutput: ws.streamRoutedModelOutput === true,
|
|
788
|
+
...(ws.xSearch ? { xSearch: ws.xSearch } : {}),
|
|
382
789
|
},
|
|
790
|
+
vision: publicVisionSidecarSettings(config, vision),
|
|
791
|
+
visionModels: vision.models,
|
|
792
|
+
// Echoed for the same reason GET always carries it: the dashboard rebuilds
|
|
793
|
+
// its sidecar state from this body, and an omitted key reads as "old
|
|
794
|
+
// server" and falls back to the full union (review F1).
|
|
795
|
+
webSearchModels: webSearchModelOptionsFrom(config, savedWebSearchCandidates),
|
|
383
796
|
});
|
|
384
797
|
}
|
|
385
798
|
|