@yansigit/opencodex 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/LICENSE +21 -0
- package/README.md +303 -0
- package/assets/architecture.png +0 -0
- package/assets/banner.png +0 -0
- package/assets/claude-code-models.gif +0 -0
- package/assets/codex-app-picker.png +0 -0
- package/bin/ocx.mjs +587 -0
- package/bin/package-main.mjs +9 -0
- package/gui/dist/assets/index-BNJ7r4Gd.js +102 -0
- package/gui/dist/assets/index-CGoDO3uO.css +1 -0
- package/gui/dist/favicon.png +0 -0
- package/gui/dist/icons.svg +24 -0
- package/gui/dist/index.html +25 -0
- package/gui/dist/logo.png +0 -0
- package/gui/dist/provider-icons/alibaba-color.svg +1 -0
- package/gui/dist/provider-icons/antigravity-color.svg +1 -0
- package/gui/dist/provider-icons/claude-color.svg +1 -0
- package/gui/dist/provider-icons/cline-color.svg +16 -0
- package/gui/dist/provider-icons/cloudflare-ai-gateway-color.svg +1 -0
- package/gui/dist/provider-icons/commandcode-color.svg +1 -0
- package/gui/dist/provider-icons/copilot-color.svg +1 -0
- package/gui/dist/provider-icons/cursor-color.svg +2 -0
- package/gui/dist/provider-icons/deepseek-color.svg +1 -0
- package/gui/dist/provider-icons/discord.svg +1 -0
- package/gui/dist/provider-icons/firepass-color.svg +1 -0
- package/gui/dist/provider-icons/fireworks-color.svg +1 -0
- package/gui/dist/provider-icons/gemini-color.svg +1 -0
- package/gui/dist/provider-icons/github-copilot-color.svg +1 -0
- package/gui/dist/provider-icons/gitlab-duo-color.svg +1 -0
- package/gui/dist/provider-icons/grok.svg +1 -0
- package/gui/dist/provider-icons/groq-color.svg +1 -0
- package/gui/dist/provider-icons/huggingface-color.svg +1 -0
- package/gui/dist/provider-icons/kimi-color.svg +1 -0
- package/gui/dist/provider-icons/kiro-color.svg +15 -0
- package/gui/dist/provider-icons/lm-studio-color.svg +1 -0
- package/gui/dist/provider-icons/mistral-color.svg +1 -0
- package/gui/dist/provider-icons/moonshot-color.svg +1 -0
- package/gui/dist/provider-icons/nvidia-color.svg +1 -0
- package/gui/dist/provider-icons/ollama-color.svg +1 -0
- package/gui/dist/provider-icons/openai.svg +1 -0
- package/gui/dist/provider-icons/opencode.svg +2 -0
- package/gui/dist/provider-icons/openrouter-color.svg +1 -0
- package/gui/dist/provider-icons/pi.svg +21 -0
- package/gui/dist/provider-icons/qianfan-color.svg +1 -0
- package/gui/dist/provider-icons/qwen-portal-color.svg +1 -0
- package/gui/dist/provider-icons/telegram.svg +1 -0
- package/gui/dist/provider-icons/vercel-ai-gateway-color.svg +1 -0
- package/gui/dist/provider-icons/vllm-color.svg +1 -0
- package/gui/dist/provider-icons/xiaomi-color.svg +1 -0
- package/package.json +108 -0
- package/src/AGENTS.md +28 -0
- package/src/adapters/anthropic-image-guard.ts +251 -0
- package/src/adapters/anthropic-image-normalize.ts +518 -0
- package/src/adapters/anthropic-output-schema.ts +137 -0
- package/src/adapters/anthropic.ts +1327 -0
- package/src/adapters/azure.ts +36 -0
- package/src/adapters/base.ts +121 -0
- package/src/adapters/client-fingerprint.ts +65 -0
- package/src/adapters/cline-pass-deepseek-v4-tool-replay.ts +69 -0
- package/src/adapters/command-code.ts +601 -0
- package/src/adapters/cursor/arg-codec.ts +38 -0
- package/src/adapters/cursor/arg-normalize.ts +104 -0
- package/src/adapters/cursor/checkpoint-store.ts +303 -0
- package/src/adapters/cursor/cursor-errors.ts +288 -0
- package/src/adapters/cursor/discovery.ts +333 -0
- package/src/adapters/cursor/effort-map.ts +151 -0
- package/src/adapters/cursor/exec-policy.ts +88 -0
- package/src/adapters/cursor/framing.ts +250 -0
- package/src/adapters/cursor/gen/agent_pb.ts +15274 -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/kv-store.ts +52 -0
- package/src/adapters/cursor/live-models.ts +269 -0
- package/src/adapters/cursor/live-smoke-gate.ts +41 -0
- package/src/adapters/cursor/live-transport.ts +1653 -0
- package/src/adapters/cursor/mcp-config.ts +42 -0
- package/src/adapters/cursor/mcp-manager.ts +333 -0
- package/src/adapters/cursor/message-mapper.ts +49 -0
- package/src/adapters/cursor/native-exec-common.ts +76 -0
- package/src/adapters/cursor/native-exec-desktop.ts +184 -0
- package/src/adapters/cursor/native-exec-fs.ts +332 -0
- package/src/adapters/cursor/native-exec-mcp.ts +153 -0
- package/src/adapters/cursor/native-exec-network.ts +43 -0
- package/src/adapters/cursor/native-exec-shell.ts +547 -0
- package/src/adapters/cursor/native-exec-tools.ts +118 -0
- package/src/adapters/cursor/native-exec.ts +663 -0
- package/src/adapters/cursor/protobuf-events.ts +1381 -0
- package/src/adapters/cursor/protobuf-request.ts +1032 -0
- package/src/adapters/cursor/request-builder.ts +461 -0
- package/src/adapters/cursor/thread-continuity.ts +67 -0
- package/src/adapters/cursor/tool-definitions.ts +735 -0
- package/src/adapters/cursor/tool-result-normalize.ts +92 -0
- package/src/adapters/cursor/transport-retry.ts +132 -0
- package/src/adapters/cursor/transport.ts +79 -0
- package/src/adapters/cursor/types.ts +79 -0
- package/src/adapters/cursor.ts +322 -0
- package/src/adapters/google-antigravity-hosts.ts +48 -0
- package/src/adapters/google-antigravity-replay.ts +827 -0
- package/src/adapters/google-antigravity-tools.ts +105 -0
- package/src/adapters/google-antigravity-wire.ts +141 -0
- package/src/adapters/google-errors.ts +92 -0
- package/src/adapters/google-http.ts +460 -0
- package/src/adapters/google-tool-schema.ts +238 -0
- package/src/adapters/google-truncation.ts +24 -0
- package/src/adapters/google-wire-compiler.ts +232 -0
- package/src/adapters/google.ts +1377 -0
- package/src/adapters/identity.ts +77 -0
- package/src/adapters/image.ts +23 -0
- package/src/adapters/kiro-constants.ts +16 -0
- package/src/adapters/kiro-errors.ts +208 -0
- package/src/adapters/kiro-events.ts +197 -0
- package/src/adapters/kiro-images.ts +129 -0
- package/src/adapters/kiro-retry.ts +312 -0
- package/src/adapters/kiro-thinking.ts +112 -0
- package/src/adapters/kiro-tool-fallback.ts +36 -0
- package/src/adapters/kiro-tools.ts +224 -0
- package/src/adapters/kiro-truncation.ts +33 -0
- package/src/adapters/kiro-wire.ts +129 -0
- package/src/adapters/kiro.ts +1936 -0
- package/src/adapters/mimo-free.ts +280 -0
- package/src/adapters/openai-chat-url.ts +11 -0
- package/src/adapters/openai-chat.ts +1980 -0
- package/src/adapters/openai-responses-url.ts +16 -0
- package/src/adapters/openai-responses.ts +1911 -0
- package/src/adapters/registry.ts +175 -0
- package/src/adapters/responses-tool-schema.ts +67 -0
- package/src/adapters/run-turn-queue.ts +114 -0
- package/src/adapters/tool-call-id.ts +119 -0
- package/src/adapters/tool-catalog-nudge.ts +154 -0
- package/src/adapters/upstream-http-error.ts +48 -0
- package/src/adapters/xai-web-search.ts +185 -0
- package/src/bridge.ts +1986 -0
- package/src/chat/inbound.ts +319 -0
- package/src/chat/outbound.ts +821 -0
- package/src/claude/agents-inject.ts +266 -0
- package/src/claude/alias.ts +149 -0
- package/src/claude/auth-detect.ts +229 -0
- package/src/claude/auth-mode-migration.ts +32 -0
- package/src/claude/auth-mode.ts +62 -0
- package/src/claude/context-windows.ts +205 -0
- package/src/claude/desktop-3p-guard.ts +35 -0
- package/src/claude/desktop-3p-paths.ts +84 -0
- package/src/claude/desktop-3p.ts +615 -0
- package/src/claude/desktop-health.ts +26 -0
- package/src/claude/desktop-profile.ts +263 -0
- package/src/claude/gateway-cache.ts +107 -0
- package/src/claude/inbound-debug.ts +163 -0
- package/src/claude/inbound.ts +578 -0
- package/src/claude/model-info.ts +174 -0
- package/src/claude/outbound.ts +926 -0
- package/src/cli/access.ts +108 -0
- package/src/cli/account-api.ts +302 -0
- package/src/cli/account-auth.ts +250 -0
- package/src/cli/account-catalog-refresh.ts +14 -0
- package/src/cli/account-extended.ts +737 -0
- package/src/cli/account-main.ts +317 -0
- package/src/cli/account.ts +299 -0
- package/src/cli/agent-driven.ts +70 -0
- package/src/cli/agent.ts +290 -0
- package/src/cli/catalog-prewarm.ts +27 -0
- package/src/cli/claude-agent-startup-sync.ts +73 -0
- package/src/cli/claude-desktop.ts +213 -0
- package/src/cli/claude.ts +355 -0
- package/src/cli/codex-log-guard-doctor.ts +103 -0
- package/src/cli/codex-shim-autorestore.ts +47 -0
- package/src/cli/codex-shim-readiness.ts +76 -0
- package/src/cli/combo.ts +127 -0
- package/src/cli/config-command.ts +209 -0
- package/src/cli/debug.ts +228 -0
- package/src/cli/dispatch.ts +585 -0
- package/src/cli/doctor.ts +1202 -0
- package/src/cli/ensure-desired-integrations.ts +152 -0
- package/src/cli/export-command.ts +213 -0
- package/src/cli/help.ts +101 -0
- package/src/cli/index.ts +973 -0
- package/src/cli/init.ts +211 -0
- package/src/cli/integrations.ts +260 -0
- package/src/cli/interactive-confirm.ts +133 -0
- 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 +245 -0
- package/src/cli/models.ts +422 -0
- package/src/cli/observe.ts +206 -0
- package/src/cli/opencode.ts +588 -0
- package/src/cli/provider-replit.ts +232 -0
- package/src/cli/provider-runtime.ts +179 -0
- package/src/cli/provider.ts +492 -0
- package/src/cli/ready.ts +301 -0
- package/src/cli/registry.ts +422 -0
- package/src/cli/replit-gateway-key-input.ts +138 -0
- package/src/cli/root.ts +86 -0
- package/src/cli/route-policy.ts +92 -0
- package/src/cli/runtime-api.ts +328 -0
- package/src/cli/star-prompt.ts +211 -0
- package/src/cli/status-oauth.ts +78 -0
- package/src/cli/status.ts +328 -0
- package/src/cli/system-command.ts +112 -0
- package/src/cli/system-restart-client.ts +146 -0
- package/src/cli/tray-proxy.ts +199 -0
- package/src/cli/v2.ts +268 -0
- package/src/cli.ts +10 -0
- package/src/clients/config-export.ts +1704 -0
- package/src/codex/account-id.ts +34 -0
- package/src/codex/account-label.ts +47 -0
- package/src/codex/account-lifecycle.ts +172 -0
- package/src/codex/account-namespace-match.ts +63 -0
- package/src/codex/account-namespaces.ts +195 -0
- package/src/codex/account-pause.ts +20 -0
- package/src/codex/account-priority.ts +83 -0
- package/src/codex/account-runtime-state.ts +31 -0
- package/src/codex/account-store.ts +544 -0
- package/src/codex/account-usability.ts +43 -0
- package/src/codex/admission.ts +256 -0
- package/src/codex/affinity-debug.ts +162 -0
- package/src/codex/agent-roles-sync.ts +225 -0
- package/src/codex/agent-roles.ts +238 -0
- package/src/codex/app-server-processes.ts +1143 -0
- package/src/codex/app-server-restart-service.ts +232 -0
- package/src/codex/auth-api.ts +2147 -0
- package/src/codex/auth-collision.ts +109 -0
- package/src/codex/auth-context.ts +665 -0
- package/src/codex/autostart-health.ts +156 -0
- package/src/codex/catalog/account-models.ts +67 -0
- package/src/codex/catalog/aggregation.ts +436 -0
- package/src/codex/catalog/bundled.ts +549 -0
- package/src/codex/catalog/effort.ts +446 -0
- package/src/codex/catalog/filesystem-evidence.ts +302 -0
- package/src/codex/catalog/kinds.ts +2 -0
- package/src/codex/catalog/metadata.ts +664 -0
- package/src/codex/catalog/native-models.ts +72 -0
- package/src/codex/catalog/parsing.ts +650 -0
- package/src/codex/catalog/provider-fetch.ts +2064 -0
- package/src/codex/catalog/sync.ts +1883 -0
- 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 +14 -0
- 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/data/upstream-models.json +830 -0
- package/src/codex/desired-state.ts +230 -0
- package/src/codex/exec-invocation.ts +22 -0
- package/src/codex/features.ts +1566 -0
- 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 +108 -0
- package/src/codex/history-provider.ts +979 -0
- package/src/codex/history-transition.ts +105 -0
- package/src/codex/history-worker.ts +220 -0
- package/src/codex/home.ts +206 -0
- package/src/codex/inject-coordination.ts +290 -0
- package/src/codex/inject.ts +1733 -0
- package/src/codex/injected-marker.ts +106 -0
- 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 +225 -0
- 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 +56 -0
- package/src/codex/main-account.ts +68 -0
- package/src/codex/management-convergence.ts +167 -0
- package/src/codex/model-cache.ts +273 -0
- 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 +144 -0
- package/src/codex/plan-from-token.ts +140 -0
- package/src/codex/plan.ts +40 -0
- package/src/codex/plugins-doctor.ts +242 -0
- package/src/codex/pool-rotation.ts +295 -0
- package/src/codex/project-config-warnings.ts +425 -0
- 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 +573 -0
- package/src/codex/refresh.ts +62 -0
- package/src/codex/reset-credit-recovery.ts +1044 -0
- package/src/codex/routing.ts +1888 -0
- package/src/codex/runtime.ts +659 -0
- package/src/codex/shim.ts +2170 -0
- package/src/codex/subagent-defaults.ts +550 -0
- package/src/codex/subagent-model-fallback.ts +784 -0
- package/src/codex/sync.ts +319 -0
- 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 +298 -0
- package/src/codex/websocket-registry.ts +100 -0
- package/src/codex/write-coordination.ts +114 -0
- package/src/combos/failover.ts +160 -0
- package/src/combos/index.ts +45 -0
- package/src/combos/request.ts +94 -0
- package/src/combos/resolve.ts +232 -0
- package/src/combos/types.ts +398 -0
- package/src/config/provider-name.ts +24 -0
- package/src/config.ts +4041 -0
- package/src/fork/register.ts +3 -0
- package/src/generated/compatibility-version.json +3116 -0
- package/src/generated/model-metadata.ts +106 -0
- package/src/github/star-state.ts +203 -0
- package/src/grok/inject.ts +530 -0
- package/src/grok/inspect.ts +45 -0
- package/src/grok/status.ts +121 -0
- package/src/grok/sync.ts +66 -0
- package/src/images/artifacts.ts +516 -0
- package/src/images/fulfill-video.ts +163 -0
- package/src/images/fulfill.ts +149 -0
- package/src/images/index.ts +4 -0
- package/src/images/loop.ts +955 -0
- package/src/images/plan.ts +143 -0
- package/src/images/synthetic-tool.ts +133 -0
- package/src/images/types.ts +41 -0
- package/src/images/xai-client.ts +141 -0
- package/src/images/xai-video-client.ts +163 -0
- package/src/index.ts +22 -0
- 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/abort.ts +146 -0
- package/src/lib/admin-secrets.ts +25 -0
- package/src/lib/admission.ts +83 -0
- package/src/lib/app-owned-memory-stores.ts +195 -0
- package/src/lib/app-owned-memory.ts +265 -0
- package/src/lib/bounded-body.ts +346 -0
- package/src/lib/bun-binary-validator.d.mts +3 -0
- package/src/lib/bun-binary-validator.mjs +18 -0
- package/src/lib/bun-runtime.ts +184 -0
- package/src/lib/bun-stream-caps.ts +130 -0
- package/src/lib/codex-restart-contract.ts +120 -0
- package/src/lib/config-ownership.ts +364 -0
- package/src/lib/crash-guard.ts +344 -0
- package/src/lib/debug-log-buffer.ts +83 -0
- package/src/lib/debug-settings.ts +108 -0
- package/src/lib/debug.ts +31 -0
- package/src/lib/destination-policy.ts +380 -0
- package/src/lib/errors.ts +406 -0
- package/src/lib/eventstream-decoder.ts +253 -0
- package/src/lib/fabric-task-execution-authority.ts +7 -0
- package/src/lib/fabric-task-host.ts +29 -0
- package/src/lib/gcp-adc.ts +341 -0
- package/src/lib/injection-debug-log.ts +58 -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/open-url.ts +25 -0
- package/src/lib/optional-shutdown-hooks.ts +57 -0
- package/src/lib/pinned-http.ts +270 -0
- package/src/lib/privacy.ts +20 -0
- package/src/lib/process-control.ts +168 -0
- package/src/lib/provider-outbound.ts +210 -0
- package/src/lib/provider-url.ts +14 -0
- package/src/lib/proxy-env.ts +18 -0
- package/src/lib/redact.ts +521 -0
- package/src/lib/retry-after.ts +55 -0
- package/src/lib/self-launch-argv.ts +15 -0
- package/src/lib/server-resource-ownership.ts +71 -0
- package/src/lib/service-secrets.ts +25 -0
- package/src/lib/shadow-call.ts +61 -0
- package/src/lib/sidecar-tracker.ts +52 -0
- package/src/lib/sse-decoder.ts +364 -0
- package/src/lib/state-store-registrations.ts +119 -0
- package/src/lib/state-store-sweeper.ts +184 -0
- package/src/lib/system-restart-contract.ts +73 -0
- package/src/lib/test-home-guard.ts +90 -0
- package/src/lib/token-estimate.ts +86 -0
- package/src/lib/tool-argument-integers.ts +202 -0
- package/src/lib/translator-budget.ts +400 -0
- package/src/lib/upstream-http-version.ts +57 -0
- package/src/lib/upstream-reachability.ts +95 -0
- package/src/lib/upstream-retry.ts +392 -0
- package/src/lib/win-exec.ts +115 -0
- package/src/lib/win-paths.ts +68 -0
- package/src/lib/windows-atomic-replace.ts +156 -0
- package/src/lib/windows-elevation.ts +773 -0
- package/src/lib/windows-secret-acl.ts +854 -0
- 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 +403 -0
- 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-routing.ts +594 -0
- package/src/oauth/anthropic.ts +188 -0
- package/src/oauth/antigravity-routing.ts +151 -0
- package/src/oauth/callback-server.ts +300 -0
- package/src/oauth/chatgpt.ts +161 -0
- package/src/oauth/command-code.ts +239 -0
- package/src/oauth/cursor.ts +252 -0
- package/src/oauth/github-copilot.ts +428 -0
- package/src/oauth/google-antigravity.ts +262 -0
- package/src/oauth/health.ts +407 -0
- package/src/oauth/index.ts +1504 -0
- package/src/oauth/key-providers.ts +124 -0
- package/src/oauth/kimi.ts +227 -0
- package/src/oauth/kiro-credentials.ts +726 -0
- package/src/oauth/kiro.ts +621 -0
- package/src/oauth/local-token-detect.ts +130 -0
- package/src/oauth/log.ts +50 -0
- package/src/oauth/login-cli.ts +223 -0
- package/src/oauth/nous.ts +798 -0
- package/src/oauth/pkce.ts +15 -0
- package/src/oauth/store.ts +728 -0
- package/src/oauth/token-guardian.ts +309 -0
- package/src/oauth/types.ts +62 -0
- package/src/oauth/xai.ts +241 -0
- package/src/providers/alibaba-region-backup.ts +75 -0
- package/src/providers/alibaba-region-migration.ts +156 -0
- package/src/providers/alibaba-region-startup.ts +36 -0
- package/src/providers/antigravity-models.ts +695 -0
- package/src/providers/antigravity-quota.ts +216 -0
- package/src/providers/api-keys.ts +140 -0
- package/src/providers/base-url-choices.ts +74 -0
- package/src/providers/codex-capacity.ts +292 -0
- package/src/providers/command-code-efforts.ts +144 -0
- package/src/providers/context-cap.ts +82 -0
- package/src/providers/cursor-pool.ts +72 -0
- package/src/providers/derive.ts +586 -0
- package/src/providers/fastwire.ts +501 -0
- package/src/providers/free-directory.ts +187 -0
- package/src/providers/github-copilot-transport.ts +56 -0
- package/src/providers/google-vertex-location.ts +14 -0
- package/src/providers/key-failover.ts +271 -0
- package/src/providers/kiro-models.ts +67 -0
- package/src/providers/label.ts +19 -0
- package/src/providers/model-discovery-limits.ts +16 -0
- package/src/providers/model-discovery.ts +449 -0
- package/src/providers/model-rename-migration.ts +255 -0
- package/src/providers/model-rename-startup.ts +28 -0
- package/src/providers/openai-sidecar.ts +243 -0
- package/src/providers/openai-tier-startup.ts +56 -0
- package/src/providers/openai-tiers.ts +423 -0
- package/src/providers/openai-virtual-models.ts +83 -0
- package/src/providers/opencode-zen-rate-limit.ts +102 -0
- package/src/providers/openrouter-routing.ts +102 -0
- package/src/providers/provider-id-rewrite.ts +185 -0
- package/src/providers/quota.ts +2345 -0
- package/src/providers/registry.ts +2918 -0
- package/src/providers/replit/constants.ts +27 -0
- package/src/providers/replit/derive.ts +85 -0
- package/src/providers/replit/headers.ts +28 -0
- package/src/providers/replit/origin.ts +55 -0
- package/src/providers/replit/pair-install-response.ts +72 -0
- package/src/providers/replit/probe.ts +199 -0
- package/src/providers/replit/setup.ts +350 -0
- package/src/providers/request-pacing.ts +310 -0
- package/src/providers/service-tier.ts +277 -0
- package/src/providers/slug-codec.ts +103 -0
- 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 +148 -0
- package/src/reasoning-effort.ts +183 -0
- package/src/responses/compaction.ts +142 -0
- 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 +838 -0
- package/src/responses/provider-continuation.ts +98 -0
- package/src/responses/provider-opaque-metadata.ts +73 -0
- package/src/responses/reasoning-envelope.ts +60 -0
- package/src/responses/reasoning-replay-cache.ts +426 -0
- package/src/responses/schema.ts +165 -0
- package/src/responses/spill-store.ts +459 -0
- package/src/responses/state.ts +1433 -0
- package/src/responses/thought-signature-replay.ts +347 -0
- package/src/responses/tool-groups.ts +19 -0
- package/src/responses/tool-search-compat.ts +301 -0
- package/src/responses/truncated-stop-reason.ts +60 -0
- package/src/router.ts +761 -0
- 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 +53 -0
- package/src/server/auth-cors.ts +751 -0
- package/src/server/background-lifecycle.ts +182 -0
- package/src/server/chat-completions.ts +442 -0
- package/src/server/chat-native-sse.ts +331 -0
- package/src/server/chat-native.ts +426 -0
- package/src/server/claude-messages.ts +1030 -0
- package/src/server/direct-local-http.ts +347 -0
- package/src/server/effort-policy.ts +190 -0
- package/src/server/github-copilot-responses-repair.ts +338 -0
- package/src/server/gui-static.ts +152 -0
- package/src/server/image-retry.ts +42 -0
- package/src/server/images.ts +568 -0
- package/src/server/index.ts +1813 -0
- package/src/server/lifecycle.ts +498 -0
- package/src/server/live.ts +717 -0
- 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 +1433 -0
- package/src/server/management/api-access.ts +141 -0
- package/src/server/management/api-key-usage.ts +193 -0
- package/src/server/management/body.ts +41 -0
- package/src/server/management/combo-routes.ts +263 -0
- package/src/server/management/config-routes.ts +835 -0
- package/src/server/management/context.ts +113 -0
- 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 +586 -0
- package/src/server/management/model-routes.ts +560 -0
- 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 +637 -0
- package/src/server/management/provider-capability-config.ts +48 -0
- package/src/server/management/provider-routes.ts +1033 -0
- package/src/server/management/replit-provider-routes.ts +86 -0
- 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 +385 -0
- package/src/server/management/shared.ts +286 -0
- package/src/server/management/sidebar-routes.ts +106 -0
- 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 +435 -0
- package/src/server/management/system-routes.ts +194 -0
- package/src/server/management/usage-summary-cache.ts +94 -0
- 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 +314 -0
- package/src/server/management-auth.ts +482 -0
- package/src/server/memory-watchdog.ts +156 -0
- package/src/server/passive-route-linker.ts +66 -0
- package/src/server/port-reclaim.ts +307 -0
- package/src/server/ports.ts +156 -0
- package/src/server/proxy-liveness.ts +328 -0
- package/src/server/readiness.ts +99 -0
- package/src/server/relay-eager.ts +353 -0
- package/src/server/relay.ts +1209 -0
- package/src/server/request-decompress.ts +239 -0
- package/src/server/request-log-conversation.ts +168 -0
- package/src/server/request-log.ts +1259 -0
- 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 +551 -0
- package/src/server/responses/compact.ts +771 -0
- package/src/server/responses/core.ts +5389 -0
- package/src/server/responses/empty-completion-guard.ts +276 -0
- package/src/server/responses/encrypted-payload.ts +331 -0
- package/src/server/responses/fetch-helpers.ts +232 -0
- package/src/server/responses/input-admission.ts +185 -0
- package/src/server/responses/pacing-overload.ts +13 -0
- package/src/server/responses/passthrough-error.ts +78 -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 +251 -0
- package/src/server/responses/upstream-error.ts +53 -0
- package/src/server/responses/ws-upstream.ts +308 -0
- package/src/server/responses-custom-tool-repair.ts +282 -0
- package/src/server/responses-image-gen-repair.ts +132 -0
- package/src/server/responses-item-id-repair.ts +272 -0
- 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 +25 -0
- package/src/server/search.ts +201 -0
- package/src/server/sse-frame-buffer.ts +292 -0
- package/src/server/sse-payload-rewrite.ts +263 -0
- package/src/server/startup-action-control.ts +315 -0
- package/src/server/startup-health-cache.ts +131 -0
- package/src/server/system-env.ts +484 -0
- package/src/server/windows-tcp-drop.ts +184 -0
- package/src/server/windows-tray-control.ts +41 -0
- package/src/server/ws-bridge.ts +472 -0
- package/src/service-manager-probe.ts +892 -0
- package/src/service.ts +3575 -0
- package/src/sidecar/auth.ts +92 -0
- package/src/sidecar/candidates.ts +83 -0
- package/src/stall-timeout.ts +20 -0
- package/src/storage/cleanup-job.ts +57 -0
- package/src/storage/cleanup.ts +3085 -0
- package/src/storage/policy-job.ts +457 -0
- package/src/storage/policy-scheduler.ts +40 -0
- package/src/storage/policy-worker.ts +59 -0
- package/src/storage/policy.ts +527 -0
- package/src/storage/restore-job.ts +299 -0
- package/src/storage/restore-worker.ts +58 -0
- package/src/storage/scanner.ts +238 -0
- package/src/storage/storage-mutation-coordinator.ts +139 -0
- package/src/storage/worker-lifecycle.ts +215 -0
- package/src/tray/assets/opencodex-tray-offline.ico +0 -0
- package/src/tray/assets/opencodex-tray-online.ico +0 -0
- package/src/tray/assets/opencodex-tray-warning.ico +0 -0
- package/src/tray/assets/opencodex-tray.png +0 -0
- package/src/tray/windows-tray.ps1 +364 -0
- package/src/tray/windows.ts +757 -0
- package/src/types/accounts.ts +37 -0
- package/src/types/config.ts +876 -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 +106 -0
- package/src/update/badge.ts +72 -0
- package/src/update/index.ts +415 -0
- package/src/update/job.ts +1887 -0
- package/src/update/notify.ts +263 -0
- package/src/update/npm-cache-preflight.d.mts +47 -0
- package/src/update/npm-cache-preflight.mjs +201 -0
- package/src/update/npm-invocation.d.mts +23 -0
- package/src/update/npm-invocation.mjs +94 -0
- package/src/update/transactional-install.d.mts +22 -0
- package/src/update/transactional-install.mjs +259 -0
- package/src/update/tray-update-plan.d.mts +18 -0
- package/src/update/tray-update-plan.mjs +38 -0
- package/src/usage/cost.ts +625 -0
- package/src/usage/debug.ts +97 -0
- package/src/usage/expected-prices.ts +416 -0
- package/src/usage/log.ts +1223 -0
- package/src/usage/summary.ts +753 -0
- package/src/usage/totals.ts +14 -0
- 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 +189 -0
- package/src/vision/backends.ts +97 -0
- package/src/vision/describe.ts +131 -0
- package/src/vision/eligibility.ts +250 -0
- package/src/vision/index.ts +681 -0
- 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 +195 -0
- package/src/web-search/backends.ts +108 -0
- package/src/web-search/exa-executor.ts +88 -0
- package/src/web-search/executor.ts +113 -0
- package/src/web-search/format-result.ts +89 -0
- package/src/web-search/gemini-executor.ts +141 -0
- package/src/web-search/index.ts +331 -0
- package/src/web-search/loop.ts +896 -0
- package/src/web-search/parse.ts +315 -0
- package/src/web-search/progress-stream.ts +342 -0
- package/src/web-search/sources.ts +60 -0
- package/src/web-search/synthetic-tool.ts +47 -0
- package/src/web-search/xai-executor.ts +219 -0
|
@@ -0,0 +1,835 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import type { CatalogModel } from "../../codex/catalog";
|
|
4
|
+
import { catalogModelSlug, invalidateCodexModelsCache, nativeContextLimits, nativeModelRows, uniqueCatalogModelsForPublicList } from "../../codex/catalog";
|
|
5
|
+
import {
|
|
6
|
+
DEFAULT_SUBAGENT_MODELS,
|
|
7
|
+
codexAutoStartEnabled,
|
|
8
|
+
hasOwnProvider,
|
|
9
|
+
isValidProviderName,
|
|
10
|
+
multiAgentGuidanceEnabled,
|
|
11
|
+
providerBaseUrlConfigError,
|
|
12
|
+
providerHeadersConfigError,
|
|
13
|
+
saveConfigPreservingClaudeCode,
|
|
14
|
+
} from "../../config";
|
|
15
|
+
import {
|
|
16
|
+
clearLoginState,
|
|
17
|
+
getLoginStatus,
|
|
18
|
+
isPublicOAuthProvider,
|
|
19
|
+
listOAuthProviders,
|
|
20
|
+
startLoginFlow,
|
|
21
|
+
submitManualLoginCode,
|
|
22
|
+
upsertOAuthProvider,
|
|
23
|
+
} from "../../oauth";
|
|
24
|
+
import { removeCredential } from "../../oauth/store";
|
|
25
|
+
import { providerDestinationResolvedError } from "../../lib/destination-policy";
|
|
26
|
+
import { isStreamMode } from "../../lib/bun-stream-caps";
|
|
27
|
+
import { shadowSourceModels } from "../../lib/shadow-call";
|
|
28
|
+
import {
|
|
29
|
+
configureAppOwnedMemoryBudget,
|
|
30
|
+
enforceAppOwnedMemoryBudget,
|
|
31
|
+
MAX_APP_OWNED_MEMORY_BUDGET_MB,
|
|
32
|
+
MIN_APP_OWNED_MEMORY_BUDGET_MB,
|
|
33
|
+
resolveAppOwnedMemoryBudgetBytes,
|
|
34
|
+
} from "../../lib/app-owned-memory";
|
|
35
|
+
import { enrichProviderFromCatalog, listKeyLoginProviders } from "../../oauth/key-providers";
|
|
36
|
+
import { deriveProviderPresets } from "../../providers/derive";
|
|
37
|
+
import { providerCodexAccountMode } from "../../providers/registry";
|
|
38
|
+
import { routedSlug, slugEquals } from "../../providers/slug-codec";
|
|
39
|
+
import { clearProviderQuotaCache, fetchProviderQuotaReports } from "../../providers/quota";
|
|
40
|
+
import { isCanonicalOpenAiForwardProvider, OPENAI_CODEX_PROVIDER_ID } from "../../providers/openai-tiers";
|
|
41
|
+
import { clearThreadAccountMap } from "../../codex/routing";
|
|
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";
|
|
48
|
+
import { DEFAULT_PROVIDER_CONTEXT_CAP, globalContextCapValue, providerContextCap, providerContextCaps, setAllProviderContextCaps, setGlobalContextCapValue, setProviderContextCap } from "../../providers/context-cap";
|
|
49
|
+
import { resolveCodexHomeDir } from "../../codex/home";
|
|
50
|
+
import { readUsageEntries } from "../../usage/log";
|
|
51
|
+
import { getUsageDebugLogEntries } from "../../usage/debug";
|
|
52
|
+
import { parseRange, parseUsageSurface, summarizeUsage } from "../../usage/summary";
|
|
53
|
+
import { stripCodexRuntimeProviderFields } from "../../codex/auth-context";
|
|
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";
|
|
80
|
+
import { getDebugLogEntries } from "../../lib/debug-log-buffer";
|
|
81
|
+
import { getInjectionDebugLogEntries } from "../../lib/injection-debug-log";
|
|
82
|
+
import {
|
|
83
|
+
clearDebugSettings,
|
|
84
|
+
clearDebugSetting,
|
|
85
|
+
getDebugSettings,
|
|
86
|
+
setDebugSettings,
|
|
87
|
+
type DebugFlag,
|
|
88
|
+
} from "../../lib/debug-settings";
|
|
89
|
+
import type { OcxClaudeCodeConfig, OcxConfig, OcxCustomModel, OcxProviderConfig } from "../../types";
|
|
90
|
+
import { drainAndShutdown } from "../lifecycle";
|
|
91
|
+
import { filterRequestLogs, getRequestLogEntries, type RequestLogEntry } from "../request-log";
|
|
92
|
+
import { estimateComboCost, estimateRequestCost, normalizeCostTokens, tokensPerSecond } from "../../usage/cost";
|
|
93
|
+
import type { PersistedUsageAttempt } from "../../usage/log";
|
|
94
|
+
import { isAllowedRequestOrigin, jsonResponse, providerManagementConfigError, publicProviderBaseUrl, safeConfigDTO } from "../auth-cors";
|
|
95
|
+
import { withProviderServiceTierDTO } from "./provider-capability-config";
|
|
96
|
+
import { applySystemEnvToggle } from "../system-env";
|
|
97
|
+
import { getCachedStartupHealth, invalidateStartupHealthCache } from "../startup-health-cache";
|
|
98
|
+
import { runWindowsTrayAction } from "../windows-tray-control";
|
|
99
|
+
import { runStartupInstallAction, type StartupInstallAction } from "../startup-action-control";
|
|
100
|
+
import { displayCodexRuntimePath, effortClampAppliesToRuntime, loadLastEffortClamp, resolveCodexRuntime } from "../../codex/runtime";
|
|
101
|
+
|
|
102
|
+
import { isPlainRecord, parseDebugLogQuery, tokPerSecondResult, unavailableCostReason, costResult, requestLogDto, stripRegistryOnlyStaticHeaders, fetchAllModels } from "./shared";
|
|
103
|
+
import type { MetricUnavailableReason, TokPerSecondResult, CostEstimateReason, CostResult, MetricSource } from "./shared";
|
|
104
|
+
import type { ManagementContext } from "./context";
|
|
105
|
+
import { readManagementJsonBody, rethrowManagementBodyTooLarge } from "./body";
|
|
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
|
+
|
|
243
|
+
export async function handleConfigRoutes(ctx: ManagementContext): Promise<Response | null> {
|
|
244
|
+
const { req, url, config, deps, convergeCodexCatalog, syncClaudeAgentDefsBestEffort } = ctx;
|
|
245
|
+
const readStartupHealth = deps.getCachedStartupHealth ?? getCachedStartupHealth;
|
|
246
|
+
if (url.pathname === "/api/config" && req.method === "GET") {
|
|
247
|
+
return jsonResponse(withProviderServiceTierDTO(safeConfigDTO(config), config));
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
if (url.pathname === "/api/config" && req.method === "PUT") {
|
|
251
|
+
return jsonResponse({ error: "Full config PUT is disabled. Use /api/providers POST for provider changes." }, 405);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
if (url.pathname === "/api/settings" && req.method === "GET") {
|
|
255
|
+
let resolved: ReturnType<typeof resolveCodexRuntime>;
|
|
256
|
+
try {
|
|
257
|
+
// Full alternative discovery (memoized) so newerAvailable warnings work.
|
|
258
|
+
resolved = resolveCodexRuntime();
|
|
259
|
+
} catch {
|
|
260
|
+
resolved = {
|
|
261
|
+
runtime: { command: "codex", version: null, source: "fallback" },
|
|
262
|
+
failures: [],
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
const lastClamp = loadLastEffortClamp();
|
|
266
|
+
const clampActive = effortClampAppliesToRuntime(lastClamp, resolved.runtime);
|
|
267
|
+
const warningParts: string[] = [];
|
|
268
|
+
if (resolved.replacedConfigured) {
|
|
269
|
+
warningParts.push(
|
|
270
|
+
`Preferred Codex runtime is unavailable; using ${displayCodexRuntimePath(resolved.runtime.command)} instead.`,
|
|
271
|
+
);
|
|
272
|
+
} else if (
|
|
273
|
+
resolved.runtime.source === "fallback"
|
|
274
|
+
&& resolved.failures.length > 0
|
|
275
|
+
&& !resolved.runtime.version
|
|
276
|
+
) {
|
|
277
|
+
warningParts.push("No validated Codex runtime found; falling back to `codex`.");
|
|
278
|
+
}
|
|
279
|
+
if (clampActive) {
|
|
280
|
+
const clampVersion = lastClamp?.runtimeVersion ?? resolved.runtime.version ?? "an older binary";
|
|
281
|
+
warningParts.push(
|
|
282
|
+
`Some reasoning effort options were hidden because OpenCodex used Codex ${clampVersion}.${resolved.newerAvailable ? " A newer Codex installation is available." : ""}`,
|
|
283
|
+
);
|
|
284
|
+
} else if (resolved.newerAvailable) {
|
|
285
|
+
warningParts.push(
|
|
286
|
+
`OpenCodex is using an older Codex binary (${resolved.runtime.version ?? "unknown"}). A newer Codex installation is available.`,
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
return jsonResponse({
|
|
290
|
+
// The dashboard renders request-log timestamps. Without this it formats them in the
|
|
291
|
+
// BROWSER's zone, so a KST proxy viewed from a UTC browser reports every request nine
|
|
292
|
+
// hours off (#725). Carried on settings rather than /api/logs because that route's
|
|
293
|
+
// array response has four consumers that would have to change with it.
|
|
294
|
+
timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
295
|
+
codexAutoStart: codexAutoStartEnabled(config),
|
|
296
|
+
port: config.port,
|
|
297
|
+
hostname: config.hostname ?? "127.0.0.1",
|
|
298
|
+
streamMode: config.streamMode ?? "auto",
|
|
299
|
+
appOwnedMemoryBudgetMb: config.appOwnedMemoryBudgetMb ?? 256,
|
|
300
|
+
codexAccountPickerEnabled: codexAccountPickerEnabled(config),
|
|
301
|
+
startupHealth: await readStartupHealth(config),
|
|
302
|
+
codexRuntime: {
|
|
303
|
+
path: displayCodexRuntimePath(resolved.runtime.command),
|
|
304
|
+
version: resolved.runtime.version,
|
|
305
|
+
source: resolved.runtime.source,
|
|
306
|
+
newerAvailable: resolved.newerAvailable
|
|
307
|
+
? {
|
|
308
|
+
path: displayCodexRuntimePath(resolved.newerAvailable.command),
|
|
309
|
+
version: resolved.newerAvailable.version,
|
|
310
|
+
}
|
|
311
|
+
: null,
|
|
312
|
+
catalogClamp: {
|
|
313
|
+
active: clampActive,
|
|
314
|
+
removedEfforts: clampActive ? (lastClamp?.removedEfforts ?? []) : [],
|
|
315
|
+
runtimeVersion: clampActive ? (lastClamp?.runtimeVersion ?? null) : null,
|
|
316
|
+
},
|
|
317
|
+
warning: warningParts.length > 0 ? warningParts.join(" ") : null,
|
|
318
|
+
},
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
if (url.pathname === "/api/startup-health" && req.method === "GET") {
|
|
323
|
+
return jsonResponse(await readStartupHealth(config));
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
if (url.pathname === "/api/startup-action" && req.method === "POST") {
|
|
327
|
+
let body: { action?: unknown; repair?: unknown };
|
|
328
|
+
try { body = await readManagementJsonBody(req); } catch (error) { rethrowManagementBodyTooLarge(error); return jsonResponse({ error: "invalid JSON body" }, 400); }
|
|
329
|
+
if (!body || !["install-service", "install-shim"].includes(String(body.action))) {
|
|
330
|
+
return jsonResponse({ error: "action must be install-service or install-shim" }, 400);
|
|
331
|
+
}
|
|
332
|
+
if (body.repair !== undefined && typeof body.repair !== "boolean") {
|
|
333
|
+
return jsonResponse({ error: "repair must be a boolean when provided" }, 400);
|
|
334
|
+
}
|
|
335
|
+
try {
|
|
336
|
+
const action = body.action as StartupInstallAction;
|
|
337
|
+
const repair = body.repair === true;
|
|
338
|
+
const result = await (deps.runStartupInstallAction ?? runStartupInstallAction)(action, { repair });
|
|
339
|
+
invalidateStartupHealthCache();
|
|
340
|
+
return jsonResponse({ ok: true, action, repair, message: result.message });
|
|
341
|
+
} catch (error) {
|
|
342
|
+
return jsonResponse({ error: error instanceof Error ? error.message : String(error) }, 500);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
if (url.pathname === "/api/windows-tray" && req.method === "GET") {
|
|
347
|
+
if (process.platform !== "win32") return jsonResponse({ supported: false, installed: false, running: false, stale: false, summary: `unsupported on ${process.platform}` });
|
|
348
|
+
try {
|
|
349
|
+
return jsonResponse(await runWindowsTrayAction("status"));
|
|
350
|
+
} catch (error) {
|
|
351
|
+
return jsonResponse({ error: error instanceof Error ? error.message : String(error) }, 500);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
if (url.pathname === "/api/windows-tray" && req.method === "POST") {
|
|
356
|
+
let body: { action?: unknown };
|
|
357
|
+
try { body = await readManagementJsonBody(req); } catch (error) { rethrowManagementBodyTooLarge(error); return jsonResponse({ error: "invalid JSON body" }, 400); }
|
|
358
|
+
if (!body || !["install", "start", "stop", "uninstall"].includes(String(body.action))) {
|
|
359
|
+
return jsonResponse({ error: "action must be install, start, stop, or uninstall" }, 400);
|
|
360
|
+
}
|
|
361
|
+
if (process.platform !== "win32") return jsonResponse({ error: "Windows tray is only supported on Windows" }, 400);
|
|
362
|
+
try {
|
|
363
|
+
const status = await runWindowsTrayAction(body.action as "install" | "start" | "stop" | "uninstall");
|
|
364
|
+
return jsonResponse({ ok: true, status });
|
|
365
|
+
} catch (error) {
|
|
366
|
+
return jsonResponse({ error: error instanceof Error ? error.message : String(error) }, 500);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
if (url.pathname === "/api/settings" && req.method === "PUT") {
|
|
371
|
+
// Each field is optional but at least one must be present; fields are
|
|
372
|
+
// validated when present. streamMode-only PUTs must work: Windows/macOS
|
|
373
|
+
// memory troubleshooting can use this persisted stream-shape escape hatch
|
|
374
|
+
// (a Windows service does not inherit shell env). A stream-shape
|
|
375
|
+
// change applies to NEW turns only — the config object is shared by
|
|
376
|
+
// reference with the request handlers, no restart needed.
|
|
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);
|
|
391
|
+
}
|
|
392
|
+
if (body.codexAutoStart !== undefined && typeof body.codexAutoStart !== "boolean") {
|
|
393
|
+
return jsonResponse({ error: "codexAutoStart boolean is required" }, 400);
|
|
394
|
+
}
|
|
395
|
+
if (body.streamMode !== undefined && !isStreamMode(body.streamMode)) {
|
|
396
|
+
return jsonResponse({ error: "streamMode must be auto, legacy-tee, or eager-relay" }, 400);
|
|
397
|
+
}
|
|
398
|
+
if (body.codexAccountPickerEnabled !== undefined
|
|
399
|
+
&& typeof body.codexAccountPickerEnabled !== "boolean") {
|
|
400
|
+
return jsonResponse({ error: "codexAccountPickerEnabled boolean is required" }, 400);
|
|
401
|
+
}
|
|
402
|
+
if (body.appOwnedMemoryBudgetMb !== undefined && (
|
|
403
|
+
typeof body.appOwnedMemoryBudgetMb !== "number"
|
|
404
|
+
|| !Number.isInteger(body.appOwnedMemoryBudgetMb)
|
|
405
|
+
|| body.appOwnedMemoryBudgetMb < MIN_APP_OWNED_MEMORY_BUDGET_MB
|
|
406
|
+
|| body.appOwnedMemoryBudgetMb > MAX_APP_OWNED_MEMORY_BUDGET_MB
|
|
407
|
+
)) {
|
|
408
|
+
return jsonResponse({ error: `appOwnedMemoryBudgetMb must be an integer from ${MIN_APP_OWNED_MEMORY_BUDGET_MB} to ${MAX_APP_OWNED_MEMORY_BUDGET_MB}` }, 400);
|
|
409
|
+
}
|
|
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;
|
|
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;
|
|
461
|
+
}
|
|
462
|
+
if (typeof body.appOwnedMemoryBudgetMb === "number") {
|
|
463
|
+
configureAppOwnedMemoryBudget(resolveAppOwnedMemoryBudgetBytes(body.appOwnedMemoryBudgetMb));
|
|
464
|
+
enforceAppOwnedMemoryBudget();
|
|
465
|
+
}
|
|
466
|
+
const catalogRefresh = pickerWasEnabled !== pickerIsEnabled
|
|
467
|
+
? await convergeCodexCatalog()
|
|
468
|
+
: undefined;
|
|
469
|
+
const catalogRefreshPending = catalogRefresh
|
|
470
|
+
? catalogRefreshIsPending(catalogRefresh)
|
|
471
|
+
: false;
|
|
472
|
+
invalidateStartupHealthCache();
|
|
473
|
+
return jsonResponse({
|
|
474
|
+
ok: true,
|
|
475
|
+
codexAutoStart: codexAutoStartEnabled(config),
|
|
476
|
+
streamMode: config.streamMode ?? "auto",
|
|
477
|
+
appOwnedMemoryBudgetMb: config.appOwnedMemoryBudgetMb ?? 256,
|
|
478
|
+
codexAccountPickerEnabled: pickerIsEnabled,
|
|
479
|
+
catalogRefreshPending,
|
|
480
|
+
startupHealth: await readStartupHealth(config),
|
|
481
|
+
});
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
if (url.pathname === "/api/diagnostics/project-config" && req.method === "GET") {
|
|
485
|
+
const { getCachedProjectConfigDiagnostics } = await import("../../codex/project-config-warnings");
|
|
486
|
+
const { warnings, grouped } = getCachedProjectConfigDiagnostics();
|
|
487
|
+
return jsonResponse({ warnings, grouped });
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
if (url.pathname === "/api/sync" && req.method === "POST") {
|
|
491
|
+
const { syncModelsToCodex } = await import("../../codex/sync");
|
|
492
|
+
const { attachStaleAppServerHint } = await import("../../codex/app-server-processes");
|
|
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);
|
|
507
|
+
return jsonResponse({
|
|
508
|
+
...attachStaleAppServerHint(result),
|
|
509
|
+
...(integrations.length > 0 ? { integrations } : {}),
|
|
510
|
+
...(result.ok ? {} : { error: result.message }),
|
|
511
|
+
}, status);
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
if (url.pathname === "/api/update/check" && req.method === "GET") {
|
|
515
|
+
const { checkForUpdate, normalizeUpdateChannel } = await import("../../update/job");
|
|
516
|
+
const rawTag = url.searchParams.get("tag");
|
|
517
|
+
if (rawTag && rawTag !== "latest" && rawTag !== "preview") {
|
|
518
|
+
return jsonResponse({ error: "tag must be latest or preview" }, 400);
|
|
519
|
+
}
|
|
520
|
+
return jsonResponse(checkForUpdate(normalizeUpdateChannel(rawTag)));
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
if (url.pathname === "/api/update/run" && req.method === "POST") {
|
|
524
|
+
const { normalizeUpdateChannel, startUpdateJob, UpdateJobError } = await import("../../update/job");
|
|
525
|
+
let body: { tag?: unknown; restart?: unknown };
|
|
526
|
+
try { body = await readManagementJsonBody(req); } catch (error) { rethrowManagementBodyTooLarge(error); return jsonResponse({ error: "invalid JSON body" }, 400); }
|
|
527
|
+
if (body.tag !== undefined && body.tag !== "latest" && body.tag !== "preview") {
|
|
528
|
+
return jsonResponse({ error: "tag must be latest or preview" }, 400);
|
|
529
|
+
}
|
|
530
|
+
if (body.restart !== undefined && typeof body.restart !== "boolean") {
|
|
531
|
+
return jsonResponse({ error: "restart boolean is required" }, 400);
|
|
532
|
+
}
|
|
533
|
+
try {
|
|
534
|
+
return jsonResponse({ ok: true, job: startUpdateJob(normalizeUpdateChannel(body.tag as string | undefined), body.restart !== false) });
|
|
535
|
+
} catch (err) {
|
|
536
|
+
if (err instanceof UpdateJobError) {
|
|
537
|
+
return jsonResponse({ error: err.message, code: err.code }, err.status);
|
|
538
|
+
}
|
|
539
|
+
return jsonResponse({ error: err instanceof Error ? err.message : String(err) }, 500);
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
if (url.pathname === "/api/update/status" && req.method === "GET") {
|
|
544
|
+
const { readUpdateJob } = await import("../../update/job");
|
|
545
|
+
const job = readUpdateJob(url.searchParams.get("jobId"));
|
|
546
|
+
if (!job) return jsonResponse({ error: "update job not found" }, 404);
|
|
547
|
+
return jsonResponse({ ok: true, job });
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
if (url.pathname === "/api/sidecar-settings" && req.method === "GET") {
|
|
551
|
+
const ws = config.webSearchSidecar ?? {};
|
|
552
|
+
const vision = await sidecarVisionResponseSettings(config);
|
|
553
|
+
const webSearchCandidates = await webSearchCandidateRows(config);
|
|
554
|
+
return jsonResponse({
|
|
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 } : {}),
|
|
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),
|
|
566
|
+
});
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
if (url.pathname === "/api/sidecar-settings" && req.method === "PUT") {
|
|
570
|
+
let raw: unknown;
|
|
571
|
+
try { raw = await readManagementJsonBody(req); } catch (error) { rethrowManagementBodyTooLarge(error); return jsonResponse({ error: "invalid JSON body" }, 400); }
|
|
572
|
+
// Strict shape (review F2): reject non-object bodies and non-object sections instead of throwing
|
|
573
|
+
// on `null` or silently accepting arrays/strings as no-op updates.
|
|
574
|
+
if (!isPlainRecord(raw)) return jsonResponse({ error: "body must be a JSON object" }, 400);
|
|
575
|
+
if (raw.webSearch !== undefined && !isPlainRecord(raw.webSearch)) return jsonResponse({ error: "webSearch must be an object" }, 400);
|
|
576
|
+
if (raw.vision !== undefined && !isPlainRecord(raw.vision)) return jsonResponse({ error: "vision must be an object" }, 400);
|
|
577
|
+
const body = raw as {
|
|
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
|
+
};
|
|
587
|
+
};
|
|
588
|
+
const WEB_SEARCH_BACKENDS_UNION = ["openai", "anthropic", "xai", "gemini", "exa"] as const;
|
|
589
|
+
if (body.webSearch && body.webSearch.backend !== undefined && body.webSearch.backend !== null
|
|
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);
|
|
599
|
+
}
|
|
600
|
+
if (body.vision && body.vision.backend !== undefined
|
|
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);
|
|
604
|
+
}
|
|
605
|
+
if (body.vision && body.vision.maxDescriptionsPerTurn !== undefined
|
|
606
|
+
&& (typeof body.vision.maxDescriptionsPerTurn !== "number"
|
|
607
|
+
|| !Number.isInteger(body.vision.maxDescriptionsPerTurn)
|
|
608
|
+
|| body.vision.maxDescriptionsPerTurn <= 0)) {
|
|
609
|
+
return jsonResponse({ error: "vision.maxDescriptionsPerTurn must be a positive integer" }, 400);
|
|
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
|
+
|
|
663
|
+
if (body.webSearch) {
|
|
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 };
|
|
680
|
+
if (typeof body.webSearch.model === "string") {
|
|
681
|
+
if (body.webSearch.model === "") delete webSearchCandidate.model;
|
|
682
|
+
else webSearchCandidate.model = body.webSearch.model;
|
|
683
|
+
}
|
|
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];
|
|
687
|
+
}
|
|
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;
|
|
750
|
+
}
|
|
751
|
+
if (body.vision) {
|
|
752
|
+
config.visionSidecar = { ...config.visionSidecar };
|
|
753
|
+
if (typeof body.vision.model === "string") {
|
|
754
|
+
if (body.vision.model === "") delete config.visionSidecar.model;
|
|
755
|
+
else config.visionSidecar.model = body.vision.model;
|
|
756
|
+
}
|
|
757
|
+
if (body.vision.backend === null) delete config.visionSidecar.backend;
|
|
758
|
+
else if (body.vision.backend === "openai" || body.vision.backend === "anthropic"
|
|
759
|
+
|| body.vision.backend === "routed") {
|
|
760
|
+
config.visionSidecar.backend = body.vision.backend;
|
|
761
|
+
}
|
|
762
|
+
if (typeof body.vision.maxDescriptionsPerTurn === "number") {
|
|
763
|
+
config.visionSidecar.maxDescriptionsPerTurn = body.vision.maxDescriptionsPerTurn;
|
|
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
|
+
}
|
|
777
|
+
}
|
|
778
|
+
saveConfigPreservingClaudeCode(config);
|
|
779
|
+
const ws = config.webSearchSidecar ?? {};
|
|
780
|
+
const vision = await sidecarVisionResponseSettings(config);
|
|
781
|
+
const savedWebSearchCandidates = await webSearchCandidateRows(config);
|
|
782
|
+
return jsonResponse({
|
|
783
|
+
ok: true,
|
|
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 } : {}),
|
|
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),
|
|
796
|
+
});
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
if (url.pathname === "/api/shadow-call-settings" && req.method === "GET") {
|
|
800
|
+
const sci = config.shadowCallIntercept ?? {};
|
|
801
|
+
return jsonResponse({
|
|
802
|
+
enabled: sci.enabled === true,
|
|
803
|
+
model: sci.model ?? "",
|
|
804
|
+
sourceModels: shadowSourceModels(sci.sourceModels),
|
|
805
|
+
});
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
if (url.pathname === "/api/shadow-call-settings" && req.method === "PUT") {
|
|
809
|
+
let raw: unknown;
|
|
810
|
+
try { raw = await readManagementJsonBody(req); } catch (error) { rethrowManagementBodyTooLarge(error); return jsonResponse({ error: "invalid JSON body" }, 400); }
|
|
811
|
+
if (!isPlainRecord(raw)) return jsonResponse({ error: "body must be a JSON object" }, 400);
|
|
812
|
+
const body = raw as { enabled?: unknown; model?: unknown };
|
|
813
|
+
if (body.enabled !== undefined && typeof body.enabled !== "boolean") {
|
|
814
|
+
return jsonResponse({ error: "enabled must be a boolean" }, 400);
|
|
815
|
+
}
|
|
816
|
+
if (body.model !== undefined && typeof body.model !== "string") {
|
|
817
|
+
return jsonResponse({ error: "model must be a string" }, 400);
|
|
818
|
+
}
|
|
819
|
+
config.shadowCallIntercept = { ...config.shadowCallIntercept };
|
|
820
|
+
if (typeof body.enabled === "boolean") config.shadowCallIntercept.enabled = body.enabled;
|
|
821
|
+
if (typeof body.model === "string") {
|
|
822
|
+
if (body.model === "") delete config.shadowCallIntercept.model;
|
|
823
|
+
else config.shadowCallIntercept.model = body.model;
|
|
824
|
+
}
|
|
825
|
+
saveConfigPreservingClaudeCode(config);
|
|
826
|
+
const sci = config.shadowCallIntercept;
|
|
827
|
+
return jsonResponse({
|
|
828
|
+
ok: true,
|
|
829
|
+
enabled: sci.enabled === true,
|
|
830
|
+
model: sci.model ?? "",
|
|
831
|
+
sourceModels: shadowSourceModels(sci.sourceModels),
|
|
832
|
+
});
|
|
833
|
+
}
|
|
834
|
+
return null;
|
|
835
|
+
}
|