@iislee/opencodex 2.11.0 → 2.35.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 +117 -22
- package/bin/ocx.mjs +245 -88
- package/bin/package-main.mjs +1 -1
- package/gui/dist/assets/index-Be1sDpWw.css +1 -0
- package/gui/dist/assets/index-D4OKgUV4.js +114 -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 +21 -10
- package/src/adapters/anthropic-output-schema.ts +137 -0
- package/src/adapters/anthropic.ts +475 -62
- package/src/adapters/base.ts +82 -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 +637 -0
- package/src/adapters/cursor/call-id.ts +44 -0
- package/src/adapters/cursor/catalog.ts +541 -0
- package/src/adapters/cursor/checkpoint-store.ts +308 -0
- package/src/adapters/cursor/cursor-errors.ts +144 -6
- package/src/adapters/cursor/discovery.ts +122 -14
- package/src/adapters/cursor/effort-map.ts +106 -4
- package/src/adapters/cursor/envelope-echo.ts +290 -0
- 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 +180 -59
- package/src/adapters/cursor/live-transport.ts +623 -170
- package/src/adapters/cursor/message-mapper.ts +4 -1
- package/src/adapters/cursor/native-exec-common.ts +23 -2
- package/src/adapters/cursor/native-exec-desktop.ts +23 -0
- 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 +5 -3
- package/src/adapters/cursor/native-exec.ts +121 -14
- package/src/adapters/cursor/protobuf-events.ts +829 -11
- package/src/adapters/cursor/protobuf-request.ts +447 -75
- package/src/adapters/cursor/request-builder.ts +271 -35
- package/src/adapters/cursor/tool-definitions.ts +252 -12
- package/src/adapters/cursor/tool-result-normalize.ts +115 -0
- package/src/adapters/cursor/transport.ts +22 -0
- package/src/adapters/cursor/types.ts +48 -1
- package/src/adapters/cursor.ts +336 -39
- package/src/adapters/exec-tool-result-normalize.ts +99 -0
- package/src/adapters/google-antigravity-replay.ts +713 -41
- package/src/adapters/google-antigravity-wire.ts +48 -10
- package/src/adapters/google-errors.ts +44 -12
- 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 +742 -86
- package/src/adapters/identity.ts +39 -6
- package/src/adapters/image.ts +1 -1
- package/src/adapters/kiro-constants.ts +27 -0
- 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 +65 -17
- package/src/adapters/kiro.ts +270 -73
- package/src/adapters/mimo-free.ts +17 -0
- package/src/adapters/openai-chat-url.ts +11 -0
- package/src/adapters/openai-chat.ts +1377 -344
- package/src/adapters/openai-responses-url.ts +16 -0
- package/src/adapters/openai-responses.ts +1164 -64
- package/src/adapters/registry.ts +175 -0
- package/src/adapters/responses-tool-schema.ts +67 -0
- package/src/adapters/run-turn-queue.ts +36 -1
- package/src/adapters/tool-call-id.ts +119 -0
- package/src/adapters/tool-catalog-nudge.ts +105 -21
- package/src/adapters/xai-tool-schema.ts +436 -0
- package/src/adapters/xai-web-search.ts +186 -0
- package/src/bridge.ts +512 -92
- package/src/chat/inbound.ts +57 -20
- package/src/chat/outbound.ts +97 -33
- package/src/claude/agents-inject.ts +40 -10
- package/src/claude/context-windows.ts +37 -14
- 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 +88 -21
- package/src/cli/access.ts +46 -3
- package/src/cli/account-api.ts +150 -19
- package/src/cli/account-auth.ts +33 -6
- package/src/cli/account-catalog-refresh.ts +14 -0
- package/src/cli/account-extended.ts +648 -28
- package/src/cli/account-main.ts +317 -0
- package/src/cli/account.ts +97 -19
- package/src/cli/agent.ts +53 -2
- package/src/cli/alias.ts +66 -0
- package/src/cli/capabilities-command.ts +94 -0
- package/src/cli/capabilities.ts +496 -0
- package/src/cli/claude-agent-startup-sync.ts +73 -0
- package/src/cli/claude-desktop.ts +60 -15
- package/src/cli/claude.ts +129 -35
- 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 +821 -0
- package/src/cli/doctor.ts +440 -56
- package/src/cli/ensure-desired-integrations.ts +152 -0
- package/src/cli/export-command.ts +46 -20
- package/src/cli/help.ts +34 -274
- package/src/cli/index.ts +417 -528
- package/src/cli/init.ts +5 -17
- package/src/cli/inspect.ts +230 -0
- 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 +130 -2
- package/src/cli/models.ts +118 -16
- package/src/cli/observe.ts +123 -11
- package/src/cli/opencode.ts +4 -2
- package/src/cli/provider-runtime.ts +34 -3
- package/src/cli/provider.ts +25 -3
- package/src/cli/ready.ts +301 -0
- package/src/cli/registry.ts +476 -0
- package/src/cli/root.ts +86 -0
- package/src/cli/route-policy.ts +92 -0
- package/src/cli/runtime-api.ts +57 -10
- package/src/cli/star-prompt.ts +71 -15
- package/src/cli/status.ts +51 -4
- package/src/cli/storage.ts +234 -0
- package/src/cli/system-command.ts +16 -0
- package/src/cli/system-restart-client.ts +146 -0
- package/src/cli/tray-proxy.ts +153 -6
- package/src/cli/usage-report.ts +184 -0
- package/src/cli/v2.ts +137 -18
- package/src/cli/version-skew.ts +46 -0
- package/src/cli.ts +1 -1
- package/src/clients/config-export.ts +1442 -23
- package/src/clients/effective-status.ts +30 -2
- package/src/clients/probes/cline.ts +81 -0
- package/src/codex/account-label.ts +35 -1
- package/src/codex/account-lifecycle.ts +130 -13
- package/src/codex/account-namespaces.ts +49 -3
- package/src/codex/account-pause.ts +2 -1
- package/src/codex/account-priority.ts +84 -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 +570 -107
- package/src/codex/app-server-restart-service.ts +232 -0
- package/src/codex/auth-api.ts +893 -246
- package/src/codex/auth-collision.ts +5 -3
- package/src/codex/auth-context.ts +348 -32
- package/src/codex/autostart-health.ts +24 -1
- package/src/codex/catalog/account-models.ts +67 -0
- package/src/codex/catalog/aggregation.ts +86 -10
- package/src/codex/catalog/bundled.ts +331 -33
- package/src/codex/catalog/effort.ts +138 -32
- package/src/codex/catalog/filesystem-evidence.ts +302 -0
- package/src/codex/catalog/kinds.ts +2 -0
- package/src/codex/catalog/metadata.ts +555 -45
- package/src/codex/catalog/native-models.ts +75 -0
- package/src/codex/catalog/parsing.ts +269 -30
- package/src/codex/catalog/provider-fetch.ts +1626 -144
- package/src/codex/catalog/sync.ts +1576 -187
- 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 +386 -0
- package/src/codex/convergence-types.ts +614 -0
- package/src/codex/convergence.ts +676 -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/desktop-app-restart.ts +355 -0
- package/src/codex/features.ts +636 -39
- package/src/codex/generation.ts +202 -0
- package/src/codex/history-job.ts +436 -0
- package/src/codex/history-lock.ts +242 -0
- package/src/codex/history-manifest.ts +112 -0
- package/src/codex/history-migration-guardian.ts +30 -24
- package/src/codex/history-provider.ts +1016 -235
- package/src/codex/history-transition.ts +105 -0
- package/src/codex/history-worker.ts +223 -0
- package/src/codex/inject-coordination.ts +373 -0
- package/src/codex/inject.ts +1114 -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 +81 -0
- package/src/codex/journal.ts +66 -4
- package/src/codex/log-guard/inspect.ts +524 -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 +360 -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 +622 -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 +675 -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 +1313 -0
- package/src/codex/prompt-lock.ts +143 -0
- package/src/codex/prompt-text-probe.ts +238 -0
- package/src/codex/quota-rejection.ts +298 -0
- package/src/codex/quota.ts +264 -21
- package/src/codex/refresh.ts +11 -2
- package/src/codex/reset-credit-recovery.ts +1044 -0
- package/src/codex/routing.ts +514 -94
- package/src/codex/runtime.ts +159 -38
- package/src/codex/shim.ts +1065 -31
- package/src/codex/subagent-model-fallback.ts +437 -43
- package/src/codex/sync.ts +191 -2
- package/src/codex/transition-state.ts +720 -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 +47 -0
- package/src/combos/index.ts +4 -0
- package/src/combos/request.ts +32 -0
- package/src/combos/types.ts +91 -9
- package/src/compatibility/index.ts +26 -0
- package/src/compatibility/manifest.ts +253 -0
- package/src/compatibility/openai-responses.ts +121 -0
- package/src/config/atomic-write.ts +219 -0
- package/src/config/paths.ts +40 -0
- package/src/config/process-state.ts +309 -0
- package/src/config/provider-name.ts +24 -0
- package/src/config/provider-validation.ts +177 -0
- package/src/config/rebase-provenance.ts +68 -0
- package/src/config.ts +1713 -709
- package/src/generated/compatibility-version.json +3324 -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 +157 -33
- package/src/images/plan.ts +24 -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-policy.ts +160 -0
- package/src/integrations/ownership.ts +155 -0
- package/src/integrations/registry.ts +166 -0
- package/src/integrations/serialize.ts +314 -0
- package/src/integrations/state.ts +433 -0
- package/src/integrations/store.ts +103 -0
- package/src/integrations/writer-lock.ts +98 -0
- package/src/integrations/writer.ts +715 -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/admin-secrets.ts +24 -0
- package/src/lib/app-owned-memory-stores.ts +22 -0
- package/src/lib/bounded-body.ts +167 -11
- 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 +26 -2
- package/src/lib/destination-policy.ts +65 -1
- package/src/lib/errors.ts +80 -5
- 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/package-tree-integrity.ts +101 -0
- package/src/lib/pinned-http.ts +145 -26
- package/src/lib/process-control.ts +6 -2
- 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/service-secrets.ts +15 -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 +253 -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 +30 -4
- package/src/oauth/chatgpt.ts +12 -1
- package/src/oauth/cline.ts +203 -0
- package/src/oauth/command-code.ts +239 -0
- package/src/oauth/cursor.ts +46 -5
- package/src/oauth/generic-account-failover.ts +231 -0
- package/src/oauth/google-antigravity.ts +35 -3
- package/src/oauth/health.ts +20 -12
- package/src/oauth/index.ts +462 -71
- package/src/oauth/key-providers.ts +25 -0
- package/src/oauth/kimi.ts +25 -3
- package/src/oauth/kiro.ts +95 -6
- package/src/oauth/local-token-detect.ts +13 -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/open-browser-choice.ts +26 -0
- package/src/oauth/store.ts +133 -26
- package/src/oauth/token-guardian.ts +9 -3
- package/src/oauth/types.ts +15 -0
- 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/auto-compact-budget.ts +65 -0
- package/src/providers/base-url-choices.ts +10 -0
- package/src/providers/codex-capacity.ts +292 -0
- package/src/providers/command-code-efforts.ts +176 -0
- package/src/providers/context-cap.ts +26 -8
- package/src/providers/cursor-pool.ts +72 -0
- package/src/providers/default-aliases.ts +65 -0
- package/src/providers/derive.ts +281 -6
- package/src/providers/fastwire.ts +507 -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 +35 -2
- package/src/providers/model-discovery-limits.ts +16 -0
- package/src/providers/model-discovery.ts +115 -22
- package/src/providers/model-presets.ts +119 -0
- package/src/providers/model-rename-migration.ts +255 -0
- package/src/providers/model-rename-startup.ts +28 -0
- package/src/providers/new-model-policy.ts +146 -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 +30 -0
- package/src/providers/quota.ts +1379 -40
- package/src/providers/registry.ts +1545 -112
- package/src/providers/request-pacing.ts +310 -0
- package/src/providers/service-tier.ts +277 -0
- package/src/providers/slug-codec.ts +94 -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 +32 -4
- package/src/reasoning-effort.ts +68 -3
- package/src/responses/apply-patch-envelope.ts +63 -0
- package/src/responses/code-mode-helper-compat.ts +50 -0
- package/src/responses/compaction.ts +26 -1
- package/src/responses/custom-tool-compat.ts +384 -0
- package/src/responses/hosted-tool-policy.ts +9 -0
- package/src/responses/namespace-tool-compat.ts +435 -0
- package/src/responses/parser.ts +225 -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 +504 -0
- package/src/responses/schema.ts +12 -2
- package/src/responses/spill-store.ts +75 -10
- package/src/responses/state.ts +659 -31
- package/src/responses/thought-signature-replay.ts +364 -0
- package/src/responses/tool-search-compat.ts +301 -0
- package/src/responses/truncated-stop-reason.ts +60 -0
- package/src/responses/turn-termination.ts +107 -0
- package/src/router.ts +405 -31
- package/src/routing/analytics.ts +379 -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 +155 -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 +289 -48
- package/src/server/background-lifecycle.ts +182 -0
- package/src/server/chat-completions.ts +154 -72
- package/src/server/chat-native-sse.ts +331 -0
- package/src/server/chat-native.ts +445 -0
- package/src/server/claude-messages.ts +160 -44
- 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 +1021 -190
- package/src/server/lifecycle.ts +336 -14
- 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 +433 -116
- package/src/server/management/api-key-usage.ts +31 -5
- package/src/server/management/body.ts +6 -0
- package/src/server/management/codex-prompt-routes.ts +570 -0
- package/src/server/management/combo-routes.ts +64 -25
- package/src/server/management/config-routes.ts +511 -51
- package/src/server/management/context.ts +89 -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 +127 -52
- package/src/server/management/model-routes.ts +461 -133
- package/src/server/management/model-rows.ts +167 -0
- package/src/server/management/native-integration-routes.ts +770 -0
- package/src/server/management/oauth-account-routes.ts +90 -8
- package/src/server/management/provider-capability-config.ts +48 -0
- package/src/server/management/provider-routes.ts +836 -159
- package/src/server/management/request-history-routes.ts +191 -0
- package/src/server/management/route-registry.ts +332 -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 +289 -33
- 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 +126 -23
- package/src/server/management-auth.ts +220 -5
- package/src/server/passive-route-linker.ts +66 -0
- package/src/server/port-reclaim.ts +1 -1
- package/src/server/ports.ts +41 -1
- package/src/server/proxy-liveness.ts +161 -10
- package/src/server/readiness.ts +99 -0
- package/src/server/relay-eager.ts +221 -54
- package/src/server/relay.ts +479 -87
- package/src/server/request-decompress.ts +113 -6
- package/src/server/request-log-conversation.ts +51 -0
- package/src/server/request-log.ts +298 -31
- 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/codex-auth-error.ts +55 -0
- package/src/server/responses/collaboration.ts +239 -36
- package/src/server/responses/combo-stream-preflight.ts +178 -0
- package/src/server/responses/compact.ts +444 -72
- package/src/server/responses/core.ts +3579 -436
- package/src/server/responses/empty-completion-guard.ts +333 -0
- package/src/server/responses/encrypted-payload.ts +62 -39
- package/src/server/responses/fetch-helpers.ts +89 -99
- package/src/server/responses/input-admission.ts +187 -0
- package/src/server/responses/pacing-overload.ts +13 -0
- package/src/server/responses/passthrough-error.ts +33 -9
- package/src/server/responses/policy-fallback.ts +178 -0
- package/src/server/responses/responses-field-backfill.ts +343 -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 +381 -0
- package/src/server/responses-custom-tool-repair.ts +363 -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 +363 -0
- package/src/server/responses-tool-search-repair.ts +267 -0
- package/src/server/responses-undeclared-tool-guard.ts +376 -0
- package/src/server/responses.ts +18 -2
- package/src/server/search.ts +78 -13
- package/src/server/sse-frame-buffer.ts +319 -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 +53 -40
- package/src/service-manager-probe.ts +892 -0
- package/src/service.ts +1153 -94
- package/src/sidecar/auth.ts +92 -0
- package/src/sidecar/candidates.ts +83 -0
- package/src/storage/cleanup.ts +2 -2
- package/src/storage/policy-job.ts +14 -4
- package/src/storage/policy.ts +88 -23
- 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 +911 -0
- package/src/types/provider.ts +614 -0
- package/src/types/request.ts +397 -0
- package/src/types/tools.ts +236 -0
- package/src/types/wire.ts +80 -0
- package/src/types.ts +107 -1237
- package/src/update/index.ts +37 -23
- package/src/update/job.ts +445 -68
- 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 +266 -0
- package/src/usage/cost.ts +0 -0
- package/src/usage/expected-prices.ts +287 -16
- package/src/usage/log.ts +666 -50
- package/src/usage/summary.ts +388 -17
- 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 +27 -16
- 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 +119 -0
- package/src/web-search/executor.ts +21 -6
- package/src/web-search/gemini-executor.ts +141 -0
- package/src/web-search/index.ts +150 -15
- package/src/web-search/loop.ts +284 -54
- 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,10 +1,11 @@
|
|
|
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,
|
|
8
|
+
deleteConfigTopLevelKey,
|
|
8
9
|
hasOwnProvider,
|
|
9
10
|
isValidProviderName,
|
|
10
11
|
multiAgentGuidanceEnabled,
|
|
@@ -37,9 +38,14 @@ import { deriveProviderPresets } from "../../providers/derive";
|
|
|
37
38
|
import { providerCodexAccountMode } from "../../providers/registry";
|
|
38
39
|
import { routedSlug, slugEquals } from "../../providers/slug-codec";
|
|
39
40
|
import { clearProviderQuotaCache, fetchProviderQuotaReports } from "../../providers/quota";
|
|
40
|
-
import { isCanonicalOpenAiForwardProvider } from "../../providers/openai-tiers";
|
|
41
|
+
import { isCanonicalOpenAiForwardProvider, OPENAI_CODEX_PROVIDER_ID } from "../../providers/openai-tiers";
|
|
41
42
|
import { clearThreadAccountMap } from "../../codex/routing";
|
|
42
43
|
import { primeCodexPoolQuotas } from "../../codex/auth-api";
|
|
44
|
+
import {
|
|
45
|
+
codexAccountPickerEnabled,
|
|
46
|
+
initializeDefaultCodexAccountNamespaces,
|
|
47
|
+
} from "../../codex/account-namespaces";
|
|
48
|
+
import { catalogRefreshIsPending } from "../../codex/catalog-refresh-status";
|
|
43
49
|
import { DEFAULT_PROVIDER_CONTEXT_CAP, globalContextCapValue, providerContextCap, providerContextCaps, setAllProviderContextCaps, setGlobalContextCapValue, setProviderContextCap } from "../../providers/context-cap";
|
|
44
50
|
import { resolveCodexHomeDir } from "../../codex/home";
|
|
45
51
|
import { readUsageEntries } from "../../usage/log";
|
|
@@ -47,6 +53,31 @@ import { getUsageDebugLogEntries } from "../../usage/debug";
|
|
|
47
53
|
import { parseRange, parseUsageSurface, summarizeUsage } from "../../usage/summary";
|
|
48
54
|
import { stripCodexRuntimeProviderFields } from "../../codex/auth-context";
|
|
49
55
|
import { getProviderRegistryEntry } from "../../providers/registry";
|
|
56
|
+
import { VISION_REASONING_EFFORTS, isVisionReasoningEffort } from "../../reasoning-effort";
|
|
57
|
+
import { normalizeVisionReasoningForModel } from "../../vision/reasoning";
|
|
58
|
+
import {
|
|
59
|
+
findAnthropicVisionProvider,
|
|
60
|
+
isValidVisionTimeoutMs,
|
|
61
|
+
MAX_VISION_TIMEOUT_MS,
|
|
62
|
+
MIN_VISION_TIMEOUT_MS,
|
|
63
|
+
resolveEffectiveVisionModel,
|
|
64
|
+
resolveMaxDescriptionsPerTurn,
|
|
65
|
+
resolveVisionBackend,
|
|
66
|
+
resolveVisionTimeoutMs,
|
|
67
|
+
} from "../../vision";
|
|
68
|
+
import {
|
|
69
|
+
visionCandidateRows,
|
|
70
|
+
visionDescriberIsProvablyBlind,
|
|
71
|
+
visionDescriberRejection,
|
|
72
|
+
visionModelOptionsFor,
|
|
73
|
+
} from "./vision-sidecar-options";
|
|
74
|
+
import {
|
|
75
|
+
webSearchCandidateRows,
|
|
76
|
+
webSearchModelIsRejected,
|
|
77
|
+
webSearchModelOptionsFrom,
|
|
78
|
+
webSearchModelRejection,
|
|
79
|
+
} from "./web-search-sidecar-options";
|
|
80
|
+
import { validateXaiSearchOptions } from "../../web-search/xai-executor";
|
|
50
81
|
import { getDebugLogEntries } from "../../lib/debug-log-buffer";
|
|
51
82
|
import { getInjectionDebugLogEntries } from "../../lib/injection-debug-log";
|
|
52
83
|
import {
|
|
@@ -62,6 +93,7 @@ import { filterRequestLogs, getRequestLogEntries, type RequestLogEntry } from ".
|
|
|
62
93
|
import { estimateComboCost, estimateRequestCost, normalizeCostTokens, tokensPerSecond } from "../../usage/cost";
|
|
63
94
|
import type { PersistedUsageAttempt } from "../../usage/log";
|
|
64
95
|
import { isAllowedRequestOrigin, jsonResponse, providerManagementConfigError, publicProviderBaseUrl, safeConfigDTO } from "../auth-cors";
|
|
96
|
+
import { withProviderServiceTierDTO } from "./provider-capability-config";
|
|
65
97
|
import { applySystemEnvToggle } from "../system-env";
|
|
66
98
|
import { getCachedStartupHealth, invalidateStartupHealthCache } from "../startup-health-cache";
|
|
67
99
|
import { runWindowsTrayAction } from "../windows-tray-control";
|
|
@@ -73,10 +105,147 @@ import type { MetricUnavailableReason, TokPerSecondResult, CostEstimateReason, C
|
|
|
73
105
|
import type { ManagementContext } from "./context";
|
|
74
106
|
import { readManagementJsonBody, rethrowManagementBodyTooLarge } from "./body";
|
|
75
107
|
|
|
108
|
+
async function sidecarVisionResponseSettings(config: OcxConfig): Promise<{
|
|
109
|
+
model: string;
|
|
110
|
+
reasoning: string;
|
|
111
|
+
models: Awaited<ReturnType<typeof visionModelOptionsFor>>;
|
|
112
|
+
}> {
|
|
113
|
+
const vs = config.visionSidecar ?? {};
|
|
114
|
+
// Match the runtime's one selected Anthropic executor for both backend fallback
|
|
115
|
+
// and catalog reachability; resolving it once prevents the two projections drifting.
|
|
116
|
+
const anthropicSidecar = findAnthropicVisionProvider(config);
|
|
117
|
+
// The routed backend reports its own namespaced model verbatim: it is the
|
|
118
|
+
// dispatched value, and collapsing it through the legacy resolver would
|
|
119
|
+
// display a describer the runtime is not using (roadmap 190).
|
|
120
|
+
const routedActive = vs.backend === "routed" && !!vs.model && vs.model.includes("/");
|
|
121
|
+
const backend = routedActive ? "routed" as const : resolveVisionBackend(vs.backend, anthropicSidecar);
|
|
122
|
+
const model = routedActive && vs.model
|
|
123
|
+
? vs.model
|
|
124
|
+
: resolveEffectiveVisionModel(config, backend === "routed" ? resolveVisionBackend(undefined, anthropicSidecar) : backend);
|
|
125
|
+
const reasoning = normalizeVisionReasoningForModel(model, vs.reasoning) ?? "low";
|
|
126
|
+
const models = await visionModelOptionsFor(config, anthropicSidecar);
|
|
127
|
+
// Display-only grandfather: a persisted id stays selectable, but the write gate
|
|
128
|
+
// remains stricter and rejects a model that is positively proven blind.
|
|
129
|
+
if (!models.some(option => option.value === model)) {
|
|
130
|
+
models.unshift({ value: model, label: model, backend });
|
|
131
|
+
}
|
|
132
|
+
return { model, reasoning, models };
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** One client's outcome from a fan-out sync. Absent from the list means "left alone". */
|
|
136
|
+
interface ClientIntegrationSyncOutcome {
|
|
137
|
+
readonly client: "grok" | "claude-desktop" | "mcode";
|
|
138
|
+
readonly ok: boolean;
|
|
139
|
+
readonly changed?: boolean;
|
|
140
|
+
readonly reason?: string;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Re-inject native clients that are switched ON and file integrations whose
|
|
145
|
+
* OpenCodex ownership record is the operator's durable opt-in.
|
|
146
|
+
*
|
|
147
|
+
* Only Codex used to run here, so a catalog change reached Codex and nothing else: a Grok
|
|
148
|
+
* fence or a written Desktop profile kept the context windows it was created with until the
|
|
149
|
+
* next `ocx start`. The startup path already gates each client on its own toggle
|
|
150
|
+
* (`src/cli/index.ts`), and this is that same fan-out for the on-demand command.
|
|
151
|
+
*
|
|
152
|
+
* A client that is OFF or never connected is omitted from the result rather than reported as skipped — the
|
|
153
|
+
* caller has to be able to tell "not touched" from "tried and failed". A client that fails
|
|
154
|
+
* does not fail the sync: Codex is the one that matters for routing, and a broken Grok file
|
|
155
|
+
* should surface as a warning, not as a 500 on a command that did its main job.
|
|
156
|
+
*/
|
|
157
|
+
async function syncEnabledClientIntegrations(
|
|
158
|
+
port: number | undefined,
|
|
159
|
+
config: OcxConfig,
|
|
160
|
+
): Promise<ClientIntegrationSyncOutcome[]> {
|
|
161
|
+
if (port === undefined) return [];
|
|
162
|
+
const { claudeDesktopIntegrationEnabled, grokIntegrationEnabled } = await import("../../codex/desired-state");
|
|
163
|
+
const out: ClientIntegrationSyncOutcome[] = [];
|
|
164
|
+
|
|
165
|
+
if (grokIntegrationEnabled(config)) {
|
|
166
|
+
try {
|
|
167
|
+
const { syncGrokConfig } = await import("../../grok/sync");
|
|
168
|
+
const r = await syncGrokConfig(port, config, config.hostname ? { hostname: config.hostname } : {});
|
|
169
|
+
out.push(r.ok
|
|
170
|
+
? { client: "grok", ok: true, changed: r.changed === true }
|
|
171
|
+
: { client: "grok", ok: false, reason: r.message });
|
|
172
|
+
} catch (error) {
|
|
173
|
+
out.push({ client: "grok", ok: false, reason: error instanceof Error ? error.message : String(error) });
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (claudeDesktopIntegrationEnabled(config)) {
|
|
178
|
+
try {
|
|
179
|
+
const { writeDesktop3pConfig } = await import("../../claude/desktop-3p");
|
|
180
|
+
const { desktopVisibleNativeSlugs, filterCatalogVisibleModels } = await import("../../codex/catalog");
|
|
181
|
+
const { fetchAllModels } = await import("../management-api");
|
|
182
|
+
const routed = filterCatalogVisibleModels(await fetchAllModels(config), config)
|
|
183
|
+
.map(model => ({ provider: model.provider, id: model.id, contextWindow: model.contextWindow }));
|
|
184
|
+
const r = writeDesktop3pConfig(
|
|
185
|
+
port,
|
|
186
|
+
[...desktopVisibleNativeSlugs(config)],
|
|
187
|
+
routed,
|
|
188
|
+
config.apiKeys?.[0]?.key,
|
|
189
|
+
"static",
|
|
190
|
+
config.claudeCode?.desktopProfile,
|
|
191
|
+
nativeContextLimits(config),
|
|
192
|
+
);
|
|
193
|
+
out.push(r.written
|
|
194
|
+
? { client: "claude-desktop", ok: true, changed: true }
|
|
195
|
+
: { client: "claude-desktop", ok: false, reason: r.reason ?? "Claude Desktop write failed" });
|
|
196
|
+
} catch (error) {
|
|
197
|
+
out.push({ client: "claude-desktop", ok: false, reason: error instanceof Error ? error.message : String(error) });
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
try {
|
|
202
|
+
const { refreshOwnedIntegration } = await import("../../integrations/owned-refresh");
|
|
203
|
+
const result = await refreshOwnedIntegration({
|
|
204
|
+
clientId: "mcode",
|
|
205
|
+
models: async () => {
|
|
206
|
+
const { loadExportModels } = await import("./model-rows");
|
|
207
|
+
return loadExportModels(config);
|
|
208
|
+
},
|
|
209
|
+
config,
|
|
210
|
+
port,
|
|
211
|
+
});
|
|
212
|
+
if (result) {
|
|
213
|
+
out.push(result.ok
|
|
214
|
+
? {
|
|
215
|
+
client: "mcode",
|
|
216
|
+
ok: true,
|
|
217
|
+
changed: result.changed === true,
|
|
218
|
+
...(result.reason ? { reason: result.reason } : {}),
|
|
219
|
+
}
|
|
220
|
+
: { client: "mcode", ok: false, reason: result.reason });
|
|
221
|
+
}
|
|
222
|
+
} catch (error) {
|
|
223
|
+
out.push({ client: "mcode", ok: false, reason: error instanceof Error ? error.message : String(error) });
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return out;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function publicVisionSidecarSettings(
|
|
230
|
+
config: OcxConfig,
|
|
231
|
+
vision: Awaited<ReturnType<typeof sidecarVisionResponseSettings>>,
|
|
232
|
+
) {
|
|
233
|
+
const vs = config.visionSidecar ?? {};
|
|
234
|
+
return {
|
|
235
|
+
enabled: vs.enabled !== false,
|
|
236
|
+
model: vision.model,
|
|
237
|
+
backend: vs.backend,
|
|
238
|
+
reasoning: vision.reasoning,
|
|
239
|
+
maxDescriptionsPerTurn: resolveMaxDescriptionsPerTurn(vs.maxDescriptionsPerTurn),
|
|
240
|
+
timeoutMs: resolveVisionTimeoutMs(vs.timeoutMs),
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
|
|
76
244
|
export async function handleConfigRoutes(ctx: ManagementContext): Promise<Response | null> {
|
|
77
|
-
const { req, url, config, deps,
|
|
245
|
+
const { req, url, config, deps, convergeCodexCatalog, syncClaudeAgentDefsBestEffort } = ctx;
|
|
246
|
+
const readStartupHealth = deps.getCachedStartupHealth ?? getCachedStartupHealth;
|
|
78
247
|
if (url.pathname === "/api/config" && req.method === "GET") {
|
|
79
|
-
return jsonResponse(safeConfigDTO(config));
|
|
248
|
+
return jsonResponse(withProviderServiceTierDTO(safeConfigDTO(config), config));
|
|
80
249
|
}
|
|
81
250
|
|
|
82
251
|
if (url.pathname === "/api/config" && req.method === "PUT") {
|
|
@@ -129,7 +298,14 @@ export async function handleConfigRoutes(ctx: ManagementContext): Promise<Respon
|
|
|
129
298
|
hostname: config.hostname ?? "127.0.0.1",
|
|
130
299
|
streamMode: config.streamMode ?? "auto",
|
|
131
300
|
appOwnedMemoryBudgetMb: config.appOwnedMemoryBudgetMb ?? 256,
|
|
132
|
-
|
|
301
|
+
codexAccountPickerEnabled: codexAccountPickerEnabled(config),
|
|
302
|
+
// Absent means hidden, so the GUI renders the switch without having to know that
|
|
303
|
+
// `undefined` and `false` mean the same thing.
|
|
304
|
+
showCodexSparkQuota: config.showCodexSparkQuota === true,
|
|
305
|
+
// Absent means the historical auto-open, so the GUI can render the toggle
|
|
306
|
+
// without having to know that `undefined` and `true` mean the same thing.
|
|
307
|
+
oauthOpenBrowser: config.oauthOpenBrowser !== false,
|
|
308
|
+
startupHealth: await readStartupHealth(config),
|
|
133
309
|
codexRuntime: {
|
|
134
310
|
path: displayCodexRuntimePath(resolved.runtime.command),
|
|
135
311
|
version: resolved.runtime.version,
|
|
@@ -151,7 +327,7 @@ export async function handleConfigRoutes(ctx: ManagementContext): Promise<Respon
|
|
|
151
327
|
}
|
|
152
328
|
|
|
153
329
|
if (url.pathname === "/api/startup-health" && req.method === "GET") {
|
|
154
|
-
return jsonResponse(await
|
|
330
|
+
return jsonResponse(await readStartupHealth(config));
|
|
155
331
|
}
|
|
156
332
|
|
|
157
333
|
if (url.pathname === "/api/startup-action" && req.method === "POST") {
|
|
@@ -205,17 +381,41 @@ export async function handleConfigRoutes(ctx: ManagementContext): Promise<Respon
|
|
|
205
381
|
// (a Windows service does not inherit shell env). A stream-shape
|
|
206
382
|
// change applies to NEW turns only — the config object is shared by
|
|
207
383
|
// reference with the request handlers, no restart needed.
|
|
208
|
-
let
|
|
209
|
-
try {
|
|
210
|
-
if (
|
|
211
|
-
|
|
384
|
+
let parsedBody: unknown;
|
|
385
|
+
try { parsedBody = await readManagementJsonBody(req); } catch (error) { rethrowManagementBodyTooLarge(error); return jsonResponse({ error: "invalid JSON body" }, 400); }
|
|
386
|
+
if (!isPlainRecord(parsedBody)) return jsonResponse({ error: "settings body must be an object" }, 400);
|
|
387
|
+
const body = parsedBody as {
|
|
388
|
+
codexAutoStart?: unknown;
|
|
389
|
+
streamMode?: unknown;
|
|
390
|
+
appOwnedMemoryBudgetMb?: unknown;
|
|
391
|
+
codexAccountPickerEnabled?: unknown;
|
|
392
|
+
oauthOpenBrowser?: unknown;
|
|
393
|
+
showCodexSparkQuota?: unknown;
|
|
394
|
+
};
|
|
395
|
+
if (body.codexAutoStart === undefined
|
|
396
|
+
&& body.streamMode === undefined
|
|
397
|
+
&& body.appOwnedMemoryBudgetMb === undefined
|
|
398
|
+
&& body.codexAccountPickerEnabled === undefined
|
|
399
|
+
&& body.oauthOpenBrowser === undefined
|
|
400
|
+
&& body.showCodexSparkQuota === undefined) {
|
|
401
|
+
return jsonResponse({ error: "provide codexAutoStart, streamMode, appOwnedMemoryBudgetMb, codexAccountPickerEnabled, oauthOpenBrowser, or showCodexSparkQuota" }, 400);
|
|
212
402
|
}
|
|
213
403
|
if (body.codexAutoStart !== undefined && typeof body.codexAutoStart !== "boolean") {
|
|
214
404
|
return jsonResponse({ error: "codexAutoStart boolean is required" }, 400);
|
|
215
405
|
}
|
|
406
|
+
if (body.oauthOpenBrowser !== undefined && typeof body.oauthOpenBrowser !== "boolean") {
|
|
407
|
+
return jsonResponse({ error: "oauthOpenBrowser boolean is required" }, 400);
|
|
408
|
+
}
|
|
216
409
|
if (body.streamMode !== undefined && !isStreamMode(body.streamMode)) {
|
|
217
410
|
return jsonResponse({ error: "streamMode must be auto, legacy-tee, or eager-relay" }, 400);
|
|
218
411
|
}
|
|
412
|
+
if (body.codexAccountPickerEnabled !== undefined
|
|
413
|
+
&& typeof body.codexAccountPickerEnabled !== "boolean") {
|
|
414
|
+
return jsonResponse({ error: "codexAccountPickerEnabled boolean is required" }, 400);
|
|
415
|
+
}
|
|
416
|
+
if (body.showCodexSparkQuota !== undefined && typeof body.showCodexSparkQuota !== "boolean") {
|
|
417
|
+
return jsonResponse({ error: "showCodexSparkQuota boolean is required" }, 400);
|
|
418
|
+
}
|
|
219
419
|
if (body.appOwnedMemoryBudgetMb !== undefined && (
|
|
220
420
|
typeof body.appOwnedMemoryBudgetMb !== "number"
|
|
221
421
|
|| !Number.isInteger(body.appOwnedMemoryBudgetMb)
|
|
@@ -224,31 +424,95 @@ export async function handleConfigRoutes(ctx: ManagementContext): Promise<Respon
|
|
|
224
424
|
)) {
|
|
225
425
|
return jsonResponse({ error: `appOwnedMemoryBudgetMb must be an integer from ${MIN_APP_OWNED_MEMORY_BUDGET_MB} to ${MAX_APP_OWNED_MEMORY_BUDGET_MB}` }, 400);
|
|
226
426
|
}
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
427
|
+
const previousSettings = {
|
|
428
|
+
codexAutoStart: config.codexAutoStart,
|
|
429
|
+
hasCodexAutoStart: Object.hasOwn(config, "codexAutoStart"),
|
|
430
|
+
streamMode: config.streamMode,
|
|
431
|
+
hasStreamMode: Object.hasOwn(config, "streamMode"),
|
|
432
|
+
appOwnedMemoryBudgetMb: config.appOwnedMemoryBudgetMb,
|
|
433
|
+
hasAppOwnedMemoryBudgetMb: Object.hasOwn(config, "appOwnedMemoryBudgetMb"),
|
|
434
|
+
codexAccountNamespaces: config.codexAccountNamespaces,
|
|
435
|
+
hasCodexAccountNamespaces: Object.hasOwn(config, "codexAccountNamespaces"),
|
|
436
|
+
codexAccountPickerEnabled: config.codexAccountPickerEnabled,
|
|
437
|
+
hasCodexAccountPickerEnabled: Object.hasOwn(config, "codexAccountPickerEnabled"),
|
|
438
|
+
oauthOpenBrowser: config.oauthOpenBrowser,
|
|
439
|
+
hasOauthOpenBrowser: Object.hasOwn(config, "oauthOpenBrowser"),
|
|
440
|
+
showCodexSparkQuota: config.showCodexSparkQuota,
|
|
441
|
+
hasShowCodexSparkQuota: Object.hasOwn(config, "showCodexSparkQuota"),
|
|
442
|
+
};
|
|
443
|
+
const pickerWasEnabled = codexAccountPickerEnabled(config);
|
|
444
|
+
let pickerIsEnabled = pickerWasEnabled;
|
|
445
|
+
try {
|
|
446
|
+
if (typeof body.codexAutoStart === "boolean") {
|
|
447
|
+
config.codexAutoStart = body.codexAutoStart;
|
|
235
448
|
}
|
|
449
|
+
if (body.streamMode !== undefined) {
|
|
450
|
+
if (body.streamMode === "auto") {
|
|
451
|
+
deleteConfigTopLevelKey(config, "streamMode");
|
|
452
|
+
} else {
|
|
453
|
+
config.streamMode = body.streamMode as "legacy-tee" | "eager-relay";
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
if (typeof body.appOwnedMemoryBudgetMb === "number") {
|
|
457
|
+
config.appOwnedMemoryBudgetMb = body.appOwnedMemoryBudgetMb;
|
|
458
|
+
}
|
|
459
|
+
if (body.codexAccountPickerEnabled === true) {
|
|
460
|
+
config.codexAccountPickerEnabled = true;
|
|
461
|
+
initializeDefaultCodexAccountNamespaces(config);
|
|
462
|
+
} else if (body.codexAccountPickerEnabled === false) {
|
|
463
|
+
config.codexAccountPickerEnabled = false;
|
|
464
|
+
}
|
|
465
|
+
if (typeof body.oauthOpenBrowser === "boolean") {
|
|
466
|
+
config.oauthOpenBrowser = body.oauthOpenBrowser;
|
|
467
|
+
}
|
|
468
|
+
if (typeof body.showCodexSparkQuota === "boolean") {
|
|
469
|
+
config.showCodexSparkQuota = body.showCodexSparkQuota;
|
|
470
|
+
}
|
|
471
|
+
pickerIsEnabled = codexAccountPickerEnabled(config);
|
|
472
|
+
(deps.saveConfigPreservingClaudeCode ?? saveConfigPreservingClaudeCode)(config);
|
|
473
|
+
} catch (error) {
|
|
474
|
+
if (previousSettings.hasCodexAutoStart) config.codexAutoStart = previousSettings.codexAutoStart;
|
|
475
|
+
else deleteConfigTopLevelKey(config, "codexAutoStart");
|
|
476
|
+
if (previousSettings.hasStreamMode) config.streamMode = previousSettings.streamMode;
|
|
477
|
+
else deleteConfigTopLevelKey(config, "streamMode");
|
|
478
|
+
if (previousSettings.hasAppOwnedMemoryBudgetMb) {
|
|
479
|
+
config.appOwnedMemoryBudgetMb = previousSettings.appOwnedMemoryBudgetMb;
|
|
480
|
+
} else deleteConfigTopLevelKey(config, "appOwnedMemoryBudgetMb");
|
|
481
|
+
if (previousSettings.hasCodexAccountNamespaces) {
|
|
482
|
+
config.codexAccountNamespaces = previousSettings.codexAccountNamespaces;
|
|
483
|
+
} else deleteConfigTopLevelKey(config, "codexAccountNamespaces");
|
|
484
|
+
if (previousSettings.hasCodexAccountPickerEnabled) {
|
|
485
|
+
config.codexAccountPickerEnabled = previousSettings.codexAccountPickerEnabled;
|
|
486
|
+
} else deleteConfigTopLevelKey(config, "codexAccountPickerEnabled");
|
|
487
|
+
if (previousSettings.hasOauthOpenBrowser) {
|
|
488
|
+
config.oauthOpenBrowser = previousSettings.oauthOpenBrowser;
|
|
489
|
+
} else deleteConfigTopLevelKey(config, "oauthOpenBrowser");
|
|
490
|
+
if (previousSettings.hasShowCodexSparkQuota) {
|
|
491
|
+
config.showCodexSparkQuota = previousSettings.showCodexSparkQuota;
|
|
492
|
+
} else deleteConfigTopLevelKey(config, "showCodexSparkQuota");
|
|
493
|
+
throw error;
|
|
236
494
|
}
|
|
237
|
-
if (typeof body.appOwnedMemoryBudgetMb === "number") {
|
|
238
|
-
config.appOwnedMemoryBudgetMb = body.appOwnedMemoryBudgetMb;
|
|
239
|
-
}
|
|
240
|
-
saveConfigPreservingClaudeCode(config);
|
|
241
495
|
if (typeof body.appOwnedMemoryBudgetMb === "number") {
|
|
242
496
|
configureAppOwnedMemoryBudget(resolveAppOwnedMemoryBudgetBytes(body.appOwnedMemoryBudgetMb));
|
|
243
497
|
enforceAppOwnedMemoryBudget();
|
|
244
498
|
}
|
|
499
|
+
const catalogRefresh = pickerWasEnabled !== pickerIsEnabled
|
|
500
|
+
? await convergeCodexCatalog()
|
|
501
|
+
: undefined;
|
|
502
|
+
const catalogRefreshPending = catalogRefresh
|
|
503
|
+
? catalogRefreshIsPending(catalogRefresh)
|
|
504
|
+
: false;
|
|
245
505
|
invalidateStartupHealthCache();
|
|
246
506
|
return jsonResponse({
|
|
247
507
|
ok: true,
|
|
248
508
|
codexAutoStart: codexAutoStartEnabled(config),
|
|
249
509
|
streamMode: config.streamMode ?? "auto",
|
|
250
510
|
appOwnedMemoryBudgetMb: config.appOwnedMemoryBudgetMb ?? 256,
|
|
251
|
-
|
|
511
|
+
codexAccountPickerEnabled: pickerIsEnabled,
|
|
512
|
+
oauthOpenBrowser: config.oauthOpenBrowser !== false,
|
|
513
|
+
catalogRefreshPending,
|
|
514
|
+
showCodexSparkQuota: config.showCodexSparkQuota === true,
|
|
515
|
+
startupHealth: await readStartupHealth(config),
|
|
252
516
|
});
|
|
253
517
|
}
|
|
254
518
|
|
|
@@ -261,11 +525,28 @@ export async function handleConfigRoutes(ctx: ManagementContext): Promise<Respon
|
|
|
261
525
|
if (url.pathname === "/api/sync" && req.method === "POST") {
|
|
262
526
|
const { syncModelsToCodex } = await import("../../codex/sync");
|
|
263
527
|
const { attachStaleAppServerHint } = await import("../../codex/app-server-processes");
|
|
264
|
-
const
|
|
528
|
+
const [{ readRuntimePort }, { loadConfig }] = await Promise.all([
|
|
529
|
+
import("../../config/process-state"),
|
|
530
|
+
import("../../config"),
|
|
531
|
+
]);
|
|
532
|
+
// Never use the server-captured startup object for a durable integration
|
|
533
|
+
// decision. A toggle may have persisted while this process was gathering.
|
|
534
|
+
const runtime = readRuntimePort(process.pid);
|
|
535
|
+
const config = loadConfig();
|
|
536
|
+
const result = await syncModelsToCodex(runtime?.port, config, null);
|
|
537
|
+
// A sync used to stop here, so a Grok fence or a Desktop profile kept whatever
|
|
538
|
+
// context windows it was written with while the Codex catalog moved on. The
|
|
539
|
+
// startup path already fans out to every enabled client; this is the same fan-out
|
|
540
|
+
// for the on-demand command. Codex goes first because the others read its catalog.
|
|
541
|
+
const integrations = result.status === "refused"
|
|
542
|
+
? []
|
|
543
|
+
: await syncEnabledClientIntegrations(runtime?.port, config);
|
|
544
|
+
const status = result.status === "refused" ? 409 : (result.status === "skipped" || result.ok ? 200 : 500);
|
|
265
545
|
return jsonResponse({
|
|
266
546
|
...attachStaleAppServerHint(result),
|
|
547
|
+
...(integrations.length > 0 ? { integrations } : {}),
|
|
267
548
|
...(result.ok ? {} : { error: result.message }),
|
|
268
|
-
},
|
|
549
|
+
}, status);
|
|
269
550
|
}
|
|
270
551
|
|
|
271
552
|
if (url.pathname === "/api/update/check" && req.method === "GET") {
|
|
@@ -306,14 +587,20 @@ export async function handleConfigRoutes(ctx: ManagementContext): Promise<Respon
|
|
|
306
587
|
|
|
307
588
|
if (url.pathname === "/api/sidecar-settings" && req.method === "GET") {
|
|
308
589
|
const ws = config.webSearchSidecar ?? {};
|
|
309
|
-
const
|
|
590
|
+
const vision = await sidecarVisionResponseSettings(config);
|
|
591
|
+
const webSearchCandidates = await webSearchCandidateRows(config);
|
|
310
592
|
return jsonResponse({
|
|
311
|
-
webSearch: {
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
593
|
+
webSearch: {
|
|
594
|
+
model: ws.model ?? "gpt-5.6-luna",
|
|
595
|
+
backend: ws.backend,
|
|
596
|
+
streamRoutedModelOutput: ws.streamRoutedModelOutput === true,
|
|
597
|
+
...(ws.xSearch ? { xSearch: ws.xSearch } : {}),
|
|
316
598
|
},
|
|
599
|
+
vision: publicVisionSidecarSettings(config, vision),
|
|
600
|
+
visionModels: vision.models,
|
|
601
|
+
// ALWAYS present: the dashboard treats an omitted list as "no filter" and
|
|
602
|
+
// falls back to the full model union, so empty must be [] (review B3).
|
|
603
|
+
webSearchModels: webSearchModelOptionsFrom(config, webSearchCandidates),
|
|
317
604
|
});
|
|
318
605
|
}
|
|
319
606
|
|
|
@@ -326,16 +613,32 @@ export async function handleConfigRoutes(ctx: ManagementContext): Promise<Respon
|
|
|
326
613
|
if (raw.webSearch !== undefined && !isPlainRecord(raw.webSearch)) return jsonResponse({ error: "webSearch must be an object" }, 400);
|
|
327
614
|
if (raw.vision !== undefined && !isPlainRecord(raw.vision)) return jsonResponse({ error: "vision must be an object" }, 400);
|
|
328
615
|
const body = raw as {
|
|
329
|
-
webSearch?: { model?: unknown; backend?: unknown; reasoning?: unknown };
|
|
330
|
-
vision?: {
|
|
616
|
+
webSearch?: { model?: unknown; backend?: unknown; reasoning?: unknown; streamRoutedModelOutput?: unknown; exaApiKey?: unknown; xSearch?: unknown };
|
|
617
|
+
vision?: {
|
|
618
|
+
model?: unknown;
|
|
619
|
+
backend?: unknown;
|
|
620
|
+
reasoning?: unknown;
|
|
621
|
+
maxDescriptionsPerTurn?: unknown;
|
|
622
|
+
enabled?: unknown;
|
|
623
|
+
timeoutMs?: unknown;
|
|
624
|
+
};
|
|
331
625
|
};
|
|
626
|
+
const WEB_SEARCH_BACKENDS_UNION = ["openai", "anthropic", "xai", "gemini", "exa"] as const;
|
|
332
627
|
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);
|
|
628
|
+
&& !WEB_SEARCH_BACKENDS_UNION.includes(body.webSearch.backend as never)) {
|
|
629
|
+
return jsonResponse({ error: "webSearch.backend must be openai, anthropic, xai, gemini, exa, or null" }, 400);
|
|
630
|
+
}
|
|
631
|
+
if (body.webSearch?.model !== undefined && typeof body.webSearch.model !== "string") {
|
|
632
|
+
return jsonResponse({ error: "webSearch.model must be a string" }, 400);
|
|
633
|
+
}
|
|
634
|
+
if (body.webSearch && body.webSearch.streamRoutedModelOutput !== undefined
|
|
635
|
+
&& typeof body.webSearch.streamRoutedModelOutput !== "boolean") {
|
|
636
|
+
return jsonResponse({ error: "webSearch.streamRoutedModelOutput must be a boolean" }, 400);
|
|
335
637
|
}
|
|
336
638
|
if (body.vision && body.vision.backend !== undefined
|
|
337
|
-
&& body.vision.backend !== null && body.vision.backend !== "openai" && body.vision.backend !== "anthropic"
|
|
338
|
-
|
|
639
|
+
&& body.vision.backend !== null && body.vision.backend !== "openai" && body.vision.backend !== "anthropic"
|
|
640
|
+
&& body.vision.backend !== "routed") {
|
|
641
|
+
return jsonResponse({ error: "vision.backend must be openai, anthropic, routed, or null" }, 400);
|
|
339
642
|
}
|
|
340
643
|
if (body.vision && body.vision.maxDescriptionsPerTurn !== undefined
|
|
341
644
|
&& (typeof body.vision.maxDescriptionsPerTurn !== "number"
|
|
@@ -343,17 +646,154 @@ export async function handleConfigRoutes(ctx: ManagementContext): Promise<Respon
|
|
|
343
646
|
|| body.vision.maxDescriptionsPerTurn <= 0)) {
|
|
344
647
|
return jsonResponse({ error: "vision.maxDescriptionsPerTurn must be a positive integer" }, 400);
|
|
345
648
|
}
|
|
649
|
+
if (body.vision && body.vision.enabled !== undefined && typeof body.vision.enabled !== "boolean") {
|
|
650
|
+
return jsonResponse({ error: "vision.enabled must be a boolean" }, 400);
|
|
651
|
+
}
|
|
652
|
+
if (body.vision && body.vision.timeoutMs !== undefined && !isValidVisionTimeoutMs(body.vision.timeoutMs)) {
|
|
653
|
+
return jsonResponse({
|
|
654
|
+
error: `vision.timeoutMs must be an integer from ${MIN_VISION_TIMEOUT_MS} to ${MAX_VISION_TIMEOUT_MS}`,
|
|
655
|
+
}, 400);
|
|
656
|
+
}
|
|
657
|
+
if (body.vision?.reasoning !== undefined && !isVisionReasoningEffort(body.vision.reasoning)) {
|
|
658
|
+
return jsonResponse({ error: `vision.reasoning must be ${VISION_REASONING_EFFORTS.join(", ")}` }, 400);
|
|
659
|
+
}
|
|
660
|
+
// Reject ONLY a model we can prove is blind. An id nothing knows about stays
|
|
661
|
+
// allowed: the operator may be ahead of our catalog, and the runtime never
|
|
662
|
+
// required catalog membership (`tests/vision-reasoning-contract.test.ts`
|
|
663
|
+
// pins `custom-vision` → 200). The catalog is read ONCE and reused for the
|
|
664
|
+
// rejection body, so a 400 cannot cost two provider fetches.
|
|
665
|
+
if (body.vision && typeof body.vision.model === "string" && body.vision.model !== "") {
|
|
666
|
+
const requested = body.vision.model;
|
|
667
|
+
const candidates = await visionCandidateRows(config);
|
|
668
|
+
const hint = body.vision.backend === "anthropic" || body.vision.backend === "openai"
|
|
669
|
+
|| body.vision.backend === "routed"
|
|
670
|
+
? body.vision.backend
|
|
671
|
+
: config.visionSidecar?.backend;
|
|
672
|
+
// Coherence (roadmap 170 r2): the forward/OAuth executors POST the model
|
|
673
|
+
// string VERBATIM, so a namespaced id on those backends persists a wire
|
|
674
|
+
// id they cannot run; and "routed" without a namespace cannot route.
|
|
675
|
+
const effectiveBackend = hint ?? "openai";
|
|
676
|
+
const namespaced = requested.includes("/");
|
|
677
|
+
if (namespaced && effectiveBackend !== "routed") {
|
|
678
|
+
return jsonResponse({ error: `vision.model "${requested}" is provider-namespaced; it requires vision.backend "routed"` }, 400);
|
|
679
|
+
}
|
|
680
|
+
if (!namespaced && effectiveBackend === "routed") {
|
|
681
|
+
return jsonResponse({ error: `vision.backend "routed" requires a provider-namespaced vision.model ("provider/model"); got "${requested}"` }, 400);
|
|
682
|
+
}
|
|
683
|
+
if (visionDescriberIsProvablyBlind(config, requested, candidates, hint)) {
|
|
684
|
+
return jsonResponse(visionDescriberRejection("vision.model", requested, config, candidates), 400);
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
let normalizedVisionReasoning: ReturnType<typeof normalizeVisionReasoningForModel>;
|
|
689
|
+
let visionReasoningTouched = false;
|
|
690
|
+
if (body.vision && (body.vision.model !== undefined || body.vision.reasoning !== undefined)) {
|
|
691
|
+
visionReasoningTouched = true;
|
|
692
|
+
const model = typeof body.vision.model === "string"
|
|
693
|
+
? (body.vision.model === "" ? "gpt-5.4-mini" : body.vision.model)
|
|
694
|
+
: (config.visionSidecar?.model || "gpt-5.4-mini");
|
|
695
|
+
const sourceReasoning = body.vision.reasoning ?? config.visionSidecar?.reasoning;
|
|
696
|
+
normalizedVisionReasoning = sourceReasoning === undefined
|
|
697
|
+
? undefined
|
|
698
|
+
: normalizeVisionReasoningForModel(model, sourceReasoning);
|
|
699
|
+
}
|
|
700
|
+
|
|
346
701
|
if (body.webSearch) {
|
|
347
|
-
|
|
702
|
+
const pairTouched = body.webSearch.model !== undefined || body.webSearch.backend !== undefined;
|
|
703
|
+
// Validate against the backend the caller SUBMITTED, across the whole
|
|
704
|
+
// union — not just openai/anthropic (#2457). The union check above has
|
|
705
|
+
// already refused unknown literals, so a surviving string is a member;
|
|
706
|
+
// Array.includes does not narrow, hence the cast. Falling back to the
|
|
707
|
+
// stored backend for xai/gemini/exa both rejected legal pairs and
|
|
708
|
+
// accepted illegal ones: a submitted gemini was checked against a stored
|
|
709
|
+
// openai. null means "unset the backend", and unset resolves to openai.
|
|
710
|
+
const submittedBackend = body.webSearch.backend;
|
|
711
|
+
const effectiveBackend = typeof submittedBackend === "string"
|
|
712
|
+
&& WEB_SEARCH_BACKENDS_UNION.includes(submittedBackend as never)
|
|
713
|
+
? submittedBackend as typeof WEB_SEARCH_BACKENDS_UNION[number]
|
|
714
|
+
: submittedBackend === null
|
|
715
|
+
? "openai"
|
|
716
|
+
: config.webSearchSidecar?.backend ?? "openai";
|
|
717
|
+
const effectiveModel = typeof body.webSearch.model === "string"
|
|
718
|
+
? body.webSearch.model || undefined
|
|
719
|
+
: config.webSearchSidecar?.model;
|
|
720
|
+
if (pairTouched && effectiveModel) {
|
|
721
|
+
const candidates = await webSearchCandidateRows(config);
|
|
722
|
+
if (webSearchModelIsRejected(effectiveBackend, effectiveModel, candidates)) {
|
|
723
|
+
return jsonResponse(webSearchModelRejection("webSearch.model", effectiveBackend, effectiveModel, candidates), 400);
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
const webSearchCandidate = { ...config.webSearchSidecar };
|
|
348
727
|
if (typeof body.webSearch.model === "string") {
|
|
349
|
-
if (body.webSearch.model === "") delete
|
|
350
|
-
else
|
|
728
|
+
if (body.webSearch.model === "") delete webSearchCandidate.model;
|
|
729
|
+
else webSearchCandidate.model = body.webSearch.model;
|
|
351
730
|
}
|
|
352
|
-
if (body.webSearch.backend === null) delete
|
|
353
|
-
else if (
|
|
354
|
-
|
|
731
|
+
if (body.webSearch.backend === null) delete webSearchCandidate.backend;
|
|
732
|
+
else if (WEB_SEARCH_BACKENDS_UNION.includes(body.webSearch.backend as never)) {
|
|
733
|
+
webSearchCandidate.backend = body.webSearch.backend as typeof WEB_SEARCH_BACKENDS_UNION[number];
|
|
355
734
|
}
|
|
356
|
-
if (typeof body.webSearch.reasoning === "string")
|
|
735
|
+
if (typeof body.webSearch.reasoning === "string") webSearchCandidate.reasoning = body.webSearch.reasoning;
|
|
736
|
+
// Operator secret for the exa backend: string sets, empty string clears. The GET
|
|
737
|
+
// payload deliberately never carries it and redact.ts strips the key from logs.
|
|
738
|
+
if (typeof body.webSearch.exaApiKey === "string") {
|
|
739
|
+
if (body.webSearch.exaApiKey === "") delete webSearchCandidate.exaApiKey;
|
|
740
|
+
else webSearchCandidate.exaApiKey = body.webSearch.exaApiKey;
|
|
741
|
+
}
|
|
742
|
+
// Opt-in x_search block (L7): null clears; an object is doc-validated before persisting.
|
|
743
|
+
if (body.webSearch.xSearch === null) delete webSearchCandidate.xSearch;
|
|
744
|
+
else if (body.webSearch.xSearch !== undefined) {
|
|
745
|
+
if (!isPlainRecord(body.webSearch.xSearch)) {
|
|
746
|
+
return jsonResponse({ error: "webSearch.xSearch must be an object or null" }, 400);
|
|
747
|
+
}
|
|
748
|
+
const x = body.webSearch.xSearch as Record<string, unknown>;
|
|
749
|
+
const allowedXSearchKeys = new Set([
|
|
750
|
+
"enabled",
|
|
751
|
+
"allowedXHandles",
|
|
752
|
+
"excludedXHandles",
|
|
753
|
+
"fromDate",
|
|
754
|
+
"toDate",
|
|
755
|
+
]);
|
|
756
|
+
const unknownKey = Object.keys(x).find(key => !allowedXSearchKeys.has(key));
|
|
757
|
+
if (unknownKey !== undefined) {
|
|
758
|
+
return jsonResponse({ error: `webSearch.xSearch.${unknownKey} is not a supported field` }, 400);
|
|
759
|
+
}
|
|
760
|
+
if (x.enabled !== undefined && typeof x.enabled !== "boolean") {
|
|
761
|
+
return jsonResponse({ error: "webSearch.xSearch.enabled must be a boolean" }, 400);
|
|
762
|
+
}
|
|
763
|
+
for (const field of ["allowedXHandles", "excludedXHandles"] as const) {
|
|
764
|
+
const value = x[field];
|
|
765
|
+
if (value !== undefined && (!Array.isArray(value) || !value.every(handle => typeof handle === "string"))) {
|
|
766
|
+
return jsonResponse({ error: `webSearch.xSearch.${field} must be an array of strings` }, 400);
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
for (const field of ["fromDate", "toDate"] as const) {
|
|
770
|
+
if (x[field] !== undefined && typeof x[field] !== "string") {
|
|
771
|
+
return jsonResponse({ error: `webSearch.xSearch.${field} must be an ISO-8601 date (YYYY-MM-DD)` }, 400);
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
const candidate = {
|
|
775
|
+
...(x.enabled === true ? { enabled: true } : {}),
|
|
776
|
+
...(x.allowedXHandles !== undefined ? { allowedXHandles: x.allowedXHandles as string[] } : {}),
|
|
777
|
+
...(x.excludedXHandles !== undefined ? { excludedXHandles: x.excludedXHandles as string[] } : {}),
|
|
778
|
+
...(x.fromDate !== undefined ? { fromDate: x.fromDate as string } : {}),
|
|
779
|
+
...(x.toDate !== undefined ? { toDate: x.toDate as string } : {}),
|
|
780
|
+
};
|
|
781
|
+
const invalid = validateXaiSearchOptions({
|
|
782
|
+
xSearch: candidate.enabled,
|
|
783
|
+
allowedXHandles: candidate.allowedXHandles,
|
|
784
|
+
excludedXHandles: candidate.excludedXHandles,
|
|
785
|
+
fromDate: candidate.fromDate,
|
|
786
|
+
toDate: candidate.toDate,
|
|
787
|
+
});
|
|
788
|
+
if (invalid) return jsonResponse({ error: `webSearch.xSearch invalid: ${invalid}` }, 400);
|
|
789
|
+
webSearchCandidate.xSearch = candidate;
|
|
790
|
+
}
|
|
791
|
+
if (typeof body.webSearch.streamRoutedModelOutput === "boolean") {
|
|
792
|
+
// `false` is the default — drop the key so config files stay minimal.
|
|
793
|
+
if (body.webSearch.streamRoutedModelOutput) webSearchCandidate.streamRoutedModelOutput = true;
|
|
794
|
+
else delete webSearchCandidate.streamRoutedModelOutput;
|
|
795
|
+
}
|
|
796
|
+
config.webSearchSidecar = webSearchCandidate;
|
|
357
797
|
}
|
|
358
798
|
if (body.vision) {
|
|
359
799
|
config.visionSidecar = { ...config.visionSidecar };
|
|
@@ -362,24 +802,44 @@ export async function handleConfigRoutes(ctx: ManagementContext): Promise<Respon
|
|
|
362
802
|
else config.visionSidecar.model = body.vision.model;
|
|
363
803
|
}
|
|
364
804
|
if (body.vision.backend === null) delete config.visionSidecar.backend;
|
|
365
|
-
else if (body.vision.backend === "openai" || body.vision.backend === "anthropic"
|
|
805
|
+
else if (body.vision.backend === "openai" || body.vision.backend === "anthropic"
|
|
806
|
+
|| body.vision.backend === "routed") {
|
|
366
807
|
config.visionSidecar.backend = body.vision.backend;
|
|
367
808
|
}
|
|
368
809
|
if (typeof body.vision.maxDescriptionsPerTurn === "number") {
|
|
369
810
|
config.visionSidecar.maxDescriptionsPerTurn = body.vision.maxDescriptionsPerTurn;
|
|
370
811
|
}
|
|
812
|
+
if (typeof body.vision.enabled === "boolean") {
|
|
813
|
+
// `true` is the default — drop the key so disable/re-enable does not rewrite the file.
|
|
814
|
+
if (body.vision.enabled) delete config.visionSidecar.enabled;
|
|
815
|
+
else config.visionSidecar.enabled = false;
|
|
816
|
+
}
|
|
817
|
+
if (typeof body.vision.timeoutMs === "number") {
|
|
818
|
+
config.visionSidecar.timeoutMs = body.vision.timeoutMs;
|
|
819
|
+
}
|
|
820
|
+
if (visionReasoningTouched) {
|
|
821
|
+
if (normalizedVisionReasoning === undefined) delete config.visionSidecar.reasoning;
|
|
822
|
+
else config.visionSidecar.reasoning = normalizedVisionReasoning;
|
|
823
|
+
}
|
|
371
824
|
}
|
|
372
825
|
saveConfigPreservingClaudeCode(config);
|
|
373
826
|
const ws = config.webSearchSidecar ?? {};
|
|
374
|
-
const
|
|
827
|
+
const vision = await sidecarVisionResponseSettings(config);
|
|
828
|
+
const savedWebSearchCandidates = await webSearchCandidateRows(config);
|
|
375
829
|
return jsonResponse({
|
|
376
830
|
ok: true,
|
|
377
|
-
webSearch: {
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
831
|
+
webSearch: {
|
|
832
|
+
model: ws.model ?? "gpt-5.6-luna",
|
|
833
|
+
backend: ws.backend,
|
|
834
|
+
streamRoutedModelOutput: ws.streamRoutedModelOutput === true,
|
|
835
|
+
...(ws.xSearch ? { xSearch: ws.xSearch } : {}),
|
|
382
836
|
},
|
|
837
|
+
vision: publicVisionSidecarSettings(config, vision),
|
|
838
|
+
visionModels: vision.models,
|
|
839
|
+
// Echoed for the same reason GET always carries it: the dashboard rebuilds
|
|
840
|
+
// its sidecar state from this body, and an omitted key reads as "old
|
|
841
|
+
// server" and falls back to the full union (review F1).
|
|
842
|
+
webSearchModels: webSearchModelOptionsFrom(config, savedWebSearchCandidates),
|
|
383
843
|
});
|
|
384
844
|
}
|
|
385
845
|
|