@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
|
@@ -15,21 +15,49 @@ import { CODEX_HOME, getCodexHome } from "./paths";
|
|
|
15
15
|
import { CODEX_UNKNOWN_USAGE_SCORE, getAccountQuota } from "./quota";
|
|
16
16
|
import {
|
|
17
17
|
canAcquireCodexQuotaProbeLease,
|
|
18
|
+
canAcquireCodexQuotaScopeProbeLease,
|
|
19
|
+
codexQuotaScopeForModel,
|
|
18
20
|
computeCodexUsageScore,
|
|
21
|
+
getCodexQuotaHealthSnapshot,
|
|
22
|
+
getEffectiveActiveCodexAccountId,
|
|
19
23
|
getPoolAccountPlan,
|
|
20
|
-
isCodexAccountInCooldown,
|
|
21
24
|
} from "./routing";
|
|
22
|
-
import {
|
|
25
|
+
import {
|
|
26
|
+
isCodexAccountUsable,
|
|
27
|
+
type CodexAccountUsabilityOptions,
|
|
28
|
+
} from "./account-usability";
|
|
23
29
|
import { isCodexAccountPaused } from "./account-pause";
|
|
24
30
|
import { slugEquals } from "../providers/slug-codec";
|
|
25
31
|
import { isThreadSpawnRequest } from "../server/effort-policy";
|
|
26
32
|
import { PROVIDER_REGISTRY } from "../providers/registry";
|
|
27
|
-
import {
|
|
33
|
+
import {
|
|
34
|
+
CODEX_FORWARD_BASE_URL,
|
|
35
|
+
OPENAI_CODEX_PROVIDER_ID,
|
|
36
|
+
isCanonicalOpenAiForwardProvider,
|
|
37
|
+
} from "../providers/openai-tiers";
|
|
28
38
|
import { routeModel, type RouteResult } from "../router";
|
|
29
39
|
import { sweepExpiredOnWrite } from "../lib/state-store-sweeper";
|
|
40
|
+
import { codexAccountNamespaceForModel } from "./account-namespace-match";
|
|
41
|
+
import { ACCOUNT_GATED_NATIVE_OPENAI_MODELS } from "./catalog/native-models";
|
|
42
|
+
import {
|
|
43
|
+
getUpstreamHostHealth,
|
|
44
|
+
normalizeUpstreamHostCircuitThreshold,
|
|
45
|
+
upstreamHostHealthKey,
|
|
46
|
+
} from "./upstream-host-health";
|
|
30
47
|
export const DEFAULT_SUBAGENT_MODEL_FALLBACK_POLL_MS = 60_000;
|
|
31
48
|
|
|
49
|
+
const CODEX_FORWARD_ORIGIN = new URL(CODEX_FORWARD_BASE_URL).origin.toLowerCase();
|
|
50
|
+
|
|
32
51
|
type SubagentQuotaPrimeFn = (config: OcxConfig, reason: string) => Promise<void>;
|
|
52
|
+
/** Side-effect-free Pool account preview for one resolved fallback candidate. */
|
|
53
|
+
export type SubagentPoolAccountPreview = (
|
|
54
|
+
modelId: string | undefined,
|
|
55
|
+
now: number,
|
|
56
|
+
modelEligibleAccountIds?: ReadonlySet<string>,
|
|
57
|
+
) => string | null;
|
|
58
|
+
export type SubagentModelEligibleAccountIds = (
|
|
59
|
+
modelId: string | undefined,
|
|
60
|
+
) => ReadonlySet<string> | undefined;
|
|
33
61
|
let subagentQuotaPrimeForTests: SubagentQuotaPrimeFn | null = null;
|
|
34
62
|
let quotaPrimeInFlight: Promise<void> | null = null;
|
|
35
63
|
|
|
@@ -68,6 +96,13 @@ function isDisabledFallbackModel(model: string, config: OcxConfig): boolean {
|
|
|
68
96
|
const slash = model.indexOf("/");
|
|
69
97
|
const provider = model.slice(0, slash);
|
|
70
98
|
const modelId = model.slice(slash + 1);
|
|
99
|
+
if (codexAccountNamespaceForModel(config.codexAccountNamespaces, model)) {
|
|
100
|
+
return disabled.some(stored =>
|
|
101
|
+
stored === model
|
|
102
|
+
|| stored === modelId
|
|
103
|
+
|| slugEquals(stored, "openai", modelId)
|
|
104
|
+
);
|
|
105
|
+
}
|
|
71
106
|
return disabled.some(stored => stored === model || slugEquals(stored, provider, modelId));
|
|
72
107
|
}
|
|
73
108
|
|
|
@@ -79,13 +114,27 @@ function pollIntervalMs(config: OcxConfig): number {
|
|
|
79
114
|
return configured;
|
|
80
115
|
}
|
|
81
116
|
|
|
82
|
-
function
|
|
117
|
+
function fallbackChainKey(model: string, namespaces: unknown): string {
|
|
118
|
+
const selector = codexAccountNamespaceForModel(namespaces, model);
|
|
119
|
+
if (!selector) return JSON.stringify(["model", model.toLowerCase()]);
|
|
120
|
+
const slash = model.indexOf("/");
|
|
121
|
+
// Selector keys are exact-case account boundaries. Keep that segment distinct while
|
|
122
|
+
// retaining legacy case-insensitive de-duplication for the native model suffix.
|
|
123
|
+
return JSON.stringify(["account", selector, model.slice(slash + 1).toLowerCase()]);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function normalizedChain(
|
|
127
|
+
primary: string,
|
|
128
|
+
config: OcxConfig,
|
|
129
|
+
extra: readonly string[] = [],
|
|
130
|
+
trailing: readonly string[] = [],
|
|
131
|
+
): string[] {
|
|
83
132
|
const chain: string[] = [];
|
|
84
133
|
const seen = new Set<string>();
|
|
85
134
|
const push = (model: string | undefined) => {
|
|
86
135
|
if (!model || model.trim() === "") return;
|
|
87
136
|
const trimmed = model.trim();
|
|
88
|
-
const key = trimmed.
|
|
137
|
+
const key = fallbackChainKey(trimmed, config.codexAccountNamespaces);
|
|
89
138
|
if (seen.has(key)) return;
|
|
90
139
|
seen.add(key);
|
|
91
140
|
chain.push(trimmed);
|
|
@@ -93,6 +142,7 @@ function normalizedChain(primary: string, config: OcxConfig, extra: readonly str
|
|
|
93
142
|
push(primary);
|
|
94
143
|
for (const model of extra) push(model);
|
|
95
144
|
for (const model of config.subagentModelFallback ?? []) push(model);
|
|
145
|
+
for (const model of trailing) push(model);
|
|
96
146
|
return chain;
|
|
97
147
|
}
|
|
98
148
|
|
|
@@ -109,8 +159,14 @@ function quotaThreshold(config: OcxConfig): number {
|
|
|
109
159
|
return threshold > 0 ? threshold : Number.POSITIVE_INFINITY;
|
|
110
160
|
}
|
|
111
161
|
|
|
162
|
+
/**
|
|
163
|
+
* The account routing would actually use, not just the persisted operator
|
|
164
|
+
* selection: round-robin, fill-first, failover, and priority preemption all
|
|
165
|
+
* move the cursor in memory only, so reading the raw field would check quota
|
|
166
|
+
* against an account this request is not going to touch.
|
|
167
|
+
*/
|
|
112
168
|
function activeCodexAccountId(config: OcxConfig): string | null {
|
|
113
|
-
return config
|
|
169
|
+
return getEffectiveActiveCodexAccountId(config) ?? null;
|
|
114
170
|
}
|
|
115
171
|
|
|
116
172
|
/**
|
|
@@ -127,9 +183,25 @@ function resolvePoolFallbackAccountId(
|
|
|
127
183
|
return activeCodexAccountId(config);
|
|
128
184
|
}
|
|
129
185
|
|
|
186
|
+
function resolveRouteFallbackAccountId(
|
|
187
|
+
route: RouteResult | null,
|
|
188
|
+
config: OcxConfig,
|
|
189
|
+
accountId?: string | null,
|
|
190
|
+
now = Date.now(),
|
|
191
|
+
poolAccountPreview?: SubagentPoolAccountPreview,
|
|
192
|
+
modelEligibleAccountIds?: ReadonlySet<string>,
|
|
193
|
+
): string | null {
|
|
194
|
+
if (route?.codexAccountId !== undefined) return route.codexAccountId;
|
|
195
|
+
if (route && isPoolCodexRoute(route) && poolAccountPreview) {
|
|
196
|
+
return poolAccountPreview(route.modelId, now, modelEligibleAccountIds);
|
|
197
|
+
}
|
|
198
|
+
return resolvePoolFallbackAccountId(config, accountId);
|
|
199
|
+
}
|
|
200
|
+
|
|
130
201
|
function isRoutableFallbackModel(model: string, config: OcxConfig): boolean {
|
|
131
202
|
const slash = model.indexOf("/");
|
|
132
203
|
if (slash > 0) {
|
|
204
|
+
if (codexAccountNamespaceForModel(config.codexAccountNamespaces, model)) return true;
|
|
133
205
|
const providerName = model.slice(0, slash);
|
|
134
206
|
if (!hasOwnProvider(config.providers, providerName)) {
|
|
135
207
|
// Allow well-known "vendor/model" ids (e.g. anthropic/claude-*) to flow as
|
|
@@ -150,7 +222,7 @@ export function isNativeModelQuotaExhausted(
|
|
|
150
222
|
): boolean {
|
|
151
223
|
const route = tryRouteFallbackModel(config, model);
|
|
152
224
|
if (!route || !isPoolCodexRoute(route)) return false;
|
|
153
|
-
const resolvedAccountId =
|
|
225
|
+
const resolvedAccountId = resolveRouteFallbackAccountId(route, config, accountId);
|
|
154
226
|
if (!resolvedAccountId) return false;
|
|
155
227
|
const quota = getAccountQuota(resolvedAccountId);
|
|
156
228
|
const usage = computeCodexUsageScore(quota, getPoolAccountPlan(config, resolvedAccountId));
|
|
@@ -167,7 +239,11 @@ export function isModelHealthBlocked(
|
|
|
167
239
|
const route = tryRouteFallbackModel(config, model);
|
|
168
240
|
const poolScoped = !!route && isPoolCodexRoute(route);
|
|
169
241
|
const health = modelHealth.get(
|
|
170
|
-
healthKey(
|
|
242
|
+
healthKey(
|
|
243
|
+
model,
|
|
244
|
+
resolveRouteFallbackAccountId(route, config, accountId),
|
|
245
|
+
poolScoped,
|
|
246
|
+
),
|
|
171
247
|
);
|
|
172
248
|
return !!health && health.unavailableUntil > now;
|
|
173
249
|
}
|
|
@@ -177,27 +253,54 @@ export function isSubagentModelUnavailable(
|
|
|
177
253
|
config: OcxConfig,
|
|
178
254
|
accountId?: string | null,
|
|
179
255
|
now = Date.now(),
|
|
256
|
+
accountUsabilityOptions?: CodexAccountUsabilityOptions,
|
|
257
|
+
poolAccountPreview?: SubagentPoolAccountPreview,
|
|
258
|
+
modelEligibleAccountIdsForModel?: SubagentModelEligibleAccountIds,
|
|
180
259
|
): boolean {
|
|
181
260
|
if (isDisabledFallbackModel(model, config)) return true;
|
|
182
261
|
if (!isRoutableFallbackModel(model, config)) return true;
|
|
183
262
|
const route = tryRouteFallbackModel(config, model);
|
|
184
263
|
if (!route || route.provider.disabled === true) return true;
|
|
185
|
-
|
|
264
|
+
const modelEligibleAccountIds = modelEligibleAccountIdsForModel?.(route.modelId);
|
|
265
|
+
const candidateAccountUsabilityOptions = modelEligibleAccountIds !== undefined
|
|
266
|
+
? {
|
|
267
|
+
...accountUsabilityOptions,
|
|
268
|
+
modelEligibleAccountIds,
|
|
269
|
+
}
|
|
270
|
+
: accountUsabilityOptions;
|
|
271
|
+
const resolvedAccountId = resolveRouteFallbackAccountId(
|
|
272
|
+
route,
|
|
273
|
+
config,
|
|
274
|
+
accountId,
|
|
275
|
+
now,
|
|
276
|
+
poolAccountPreview,
|
|
277
|
+
candidateAccountUsabilityOptions?.modelEligibleAccountIds,
|
|
278
|
+
);
|
|
279
|
+
if (isModelHealthBlocked(model, config, resolvedAccountId, now)) return true;
|
|
186
280
|
if (!isPoolCodexRoute(route)) return false;
|
|
187
281
|
|
|
188
282
|
// Pool candidates need a usable account. Derive requirement from the resolved
|
|
189
283
|
// route (canonical openai defaults to pool even when codexAccountMode is omitted).
|
|
190
|
-
const resolvedAccountId = resolvePoolFallbackAccountId(config, accountId);
|
|
191
284
|
if (!resolvedAccountId) return true;
|
|
192
285
|
if (isCodexAccountPaused(config, resolvedAccountId)) return true;
|
|
193
|
-
if (!isCodexAccountUsable(config, resolvedAccountId)) return true;
|
|
194
|
-
if (
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
286
|
+
if (!isCodexAccountUsable(config, resolvedAccountId, candidateAccountUsabilityOptions)) return true;
|
|
287
|
+
if (route.codexAccountId !== undefined) {
|
|
288
|
+
// An account-qualified route is pinned and cannot consume Pool's recovery-probe
|
|
289
|
+
// escape hatch. Honor both account-wide and model-scoped cooldowns so fallback
|
|
290
|
+
// advances instead of selecting a candidate that exact auth will reject.
|
|
291
|
+
const quotaScope = codexQuotaScopeForModel(route.modelId);
|
|
292
|
+
if (getCodexQuotaHealthSnapshot(resolvedAccountId, quotaScope, now) !== null) return true;
|
|
293
|
+
} else {
|
|
294
|
+
const quotaScope = codexQuotaScopeForModel(route.modelId);
|
|
295
|
+
const cooldown = getCodexQuotaHealthSnapshot(resolvedAccountId, quotaScope, now);
|
|
296
|
+
if (cooldown !== null) {
|
|
297
|
+
const probeAvailable = cooldown.quotaScope
|
|
298
|
+
? canAcquireCodexQuotaScopeProbeLease(resolvedAccountId, cooldown.quotaScope, now)
|
|
299
|
+
: canAcquireCodexQuotaProbeLease(resolvedAccountId, now);
|
|
300
|
+
if (!probeAvailable) return true;
|
|
301
|
+
}
|
|
199
302
|
}
|
|
200
|
-
return isNativeModelQuotaExhausted(model, config,
|
|
303
|
+
return isNativeModelQuotaExhausted(model, config, resolvedAccountId, now);
|
|
201
304
|
}
|
|
202
305
|
|
|
203
306
|
export function selectAvailableSubagentModel(
|
|
@@ -207,8 +310,13 @@ export function selectAvailableSubagentModel(
|
|
|
207
310
|
accountId?: string | null,
|
|
208
311
|
now = Date.now(),
|
|
209
312
|
nativeFallbackOnly = false,
|
|
313
|
+
accountUsabilityOptions?: CodexAccountUsabilityOptions,
|
|
314
|
+
trailingFallback: readonly string[] = [],
|
|
315
|
+
poolAccountPreview?: SubagentPoolAccountPreview,
|
|
316
|
+
modelEligibleAccountIdsForModel?: SubagentModelEligibleAccountIds,
|
|
317
|
+
resolvedChain?: readonly string[],
|
|
210
318
|
): { model: string; rewritten: boolean; skipped: string[] } {
|
|
211
|
-
const chain = normalizedChain(primary, config, extraFallback);
|
|
319
|
+
const chain = resolvedChain ?? normalizedChain(primary, config, extraFallback, trailingFallback);
|
|
212
320
|
const skipped: string[] = [];
|
|
213
321
|
for (const candidate of chain) {
|
|
214
322
|
if (nativeFallbackOnly) {
|
|
@@ -218,7 +326,15 @@ export function selectAvailableSubagentModel(
|
|
|
218
326
|
continue;
|
|
219
327
|
}
|
|
220
328
|
}
|
|
221
|
-
if (isSubagentModelUnavailable(
|
|
329
|
+
if (isSubagentModelUnavailable(
|
|
330
|
+
candidate,
|
|
331
|
+
config,
|
|
332
|
+
accountId,
|
|
333
|
+
now,
|
|
334
|
+
accountUsabilityOptions,
|
|
335
|
+
poolAccountPreview,
|
|
336
|
+
modelEligibleAccountIdsForModel,
|
|
337
|
+
)) {
|
|
222
338
|
skipped.push(candidate);
|
|
223
339
|
continue;
|
|
224
340
|
}
|
|
@@ -240,7 +356,11 @@ export function noteSubagentModelFailure(
|
|
|
240
356
|
const route = tryRouteFallbackModel(config, model);
|
|
241
357
|
const poolScoped = !!route && isPoolCodexRoute(route);
|
|
242
358
|
modelHealth.set(
|
|
243
|
-
healthKey(
|
|
359
|
+
healthKey(
|
|
360
|
+
model,
|
|
361
|
+
resolveRouteFallbackAccountId(route, config, accountId),
|
|
362
|
+
poolScoped,
|
|
363
|
+
),
|
|
244
364
|
{
|
|
245
365
|
unavailableUntil: now + interval,
|
|
246
366
|
reason: "quota_exhausted",
|
|
@@ -324,13 +444,16 @@ export function readCodexAgentModel(role: string, codexHome = CODEX_HOME): strin
|
|
|
324
444
|
export function resolveAgentModelFallbackForPrimary(
|
|
325
445
|
primary: string,
|
|
326
446
|
codexHome = CODEX_HOME,
|
|
447
|
+
namespaces?: unknown,
|
|
327
448
|
): string[] {
|
|
328
449
|
const merged: string[] = [];
|
|
329
450
|
const seen = new Set<string>();
|
|
330
451
|
const push = (model: string | null | undefined) => {
|
|
331
452
|
if (!model || model.trim() === "") return;
|
|
332
453
|
const trimmed = model.trim();
|
|
333
|
-
|
|
454
|
+
// Match global-chain de-dupe: keep account-selector prefixes case-sensitive when
|
|
455
|
+
// those selectors are configured, while ordinary provider/model ids stay case-insensitive.
|
|
456
|
+
const key = fallbackChainKey(trimmed, namespaces);
|
|
334
457
|
if (seen.has(key)) return;
|
|
335
458
|
seen.add(key);
|
|
336
459
|
merged.push(trimmed);
|
|
@@ -343,18 +466,66 @@ export function resolveAgentModelFallbackForPrimary(
|
|
|
343
466
|
return merged;
|
|
344
467
|
}
|
|
345
468
|
|
|
469
|
+
function subagentQuotaPrimeBlockedByHostCircuit(config: OcxConfig): boolean {
|
|
470
|
+
if (normalizeUpstreamHostCircuitThreshold(config.upstreamHostCircuitThreshold) === 0) return false;
|
|
471
|
+
const key = upstreamHostHealthKey(OPENAI_CODEX_PROVIDER_ID, CODEX_FORWARD_ORIGIN);
|
|
472
|
+
return getUpstreamHostHealth(key)?.cooldownUntil !== undefined;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
/**
|
|
476
|
+
* Per-primary-model fallback chains from opencodex config (#1190).
|
|
477
|
+
*
|
|
478
|
+
* Storing `model_fallback` inside `$CODEX_HOME/agents/*.toml` makes Codex >= 0.146
|
|
479
|
+
* reject the whole role file as an unknown field. The config-keyed map is the
|
|
480
|
+
* supported home for that metadata; keys match the requested primary model id,
|
|
481
|
+
* using the same raw/encoded slug tolerance as the TOML role lookup.
|
|
482
|
+
*/
|
|
483
|
+
export function resolveConfiguredModelFallbackForPrimary(
|
|
484
|
+
primary: string,
|
|
485
|
+
config: OcxConfig,
|
|
486
|
+
): string[] {
|
|
487
|
+
const byModel = config.subagentModelFallbackByModel;
|
|
488
|
+
if (!byModel || typeof byModel !== "object") return [];
|
|
489
|
+
const entries: string[] = [];
|
|
490
|
+
const seen = new Set<string>();
|
|
491
|
+
const push = (model: string) => {
|
|
492
|
+
const trimmed = model.trim();
|
|
493
|
+
if (trimmed === "") return;
|
|
494
|
+
const key = fallbackChainKey(trimmed, config.codexAccountNamespaces);
|
|
495
|
+
if (seen.has(key)) return;
|
|
496
|
+
seen.add(key);
|
|
497
|
+
entries.push(trimmed);
|
|
498
|
+
};
|
|
499
|
+
for (const [key, chain] of Object.entries(byModel)) {
|
|
500
|
+
if (!slugsEquivalent(key, primary)) continue;
|
|
501
|
+
for (const model of chain) push(model);
|
|
502
|
+
}
|
|
503
|
+
return entries;
|
|
504
|
+
}
|
|
505
|
+
|
|
346
506
|
/**
|
|
347
507
|
* Best-effort quota refresh before subagent model selection.
|
|
348
508
|
* Concurrent callers share one in-flight promise. The success TTL is updated only
|
|
349
509
|
* after a successful refresh so failures remain retryable. Errors are swallowed so
|
|
350
|
-
* spawn routing can continue.
|
|
510
|
+
* spawn routing can continue. When the canonical ChatGPT origin is circuit-blocked,
|
|
511
|
+
* cached quota is used instead of sending credential-bearing usage probes to the
|
|
512
|
+
* same origin before the request's final host admission check.
|
|
351
513
|
*/
|
|
352
|
-
export function maybePrimeSubagentQuota(
|
|
514
|
+
export function maybePrimeSubagentQuota(
|
|
515
|
+
config: OcxConfig,
|
|
516
|
+
now = Date.now(),
|
|
517
|
+
options: { nativeMainReadsForbidden?: boolean } = {},
|
|
518
|
+
): Promise<void> {
|
|
519
|
+
if (options.nativeMainReadsForbidden) return Promise.resolve();
|
|
520
|
+
if (subagentQuotaPrimeBlockedByHostCircuit(config)) return Promise.resolve();
|
|
353
521
|
if (quotaPrimeInFlight) return quotaPrimeInFlight;
|
|
354
522
|
if (!shouldPrimeSubagentQuota(config, now)) return Promise.resolve();
|
|
355
523
|
|
|
356
524
|
quotaPrimeInFlight = (async () => {
|
|
357
525
|
try {
|
|
526
|
+
// Re-check after claiming single-flight ownership so a circuit opened by
|
|
527
|
+
// a concurrent request cannot race us into a fresh usage-probe pass.
|
|
528
|
+
if (subagentQuotaPrimeBlockedByHostCircuit(config)) return;
|
|
358
529
|
if (subagentQuotaPrimeForTests) {
|
|
359
530
|
await subagentQuotaPrimeForTests(config, "subagent-spawn");
|
|
360
531
|
} else {
|
|
@@ -391,18 +562,28 @@ export function applySubagentModelFallback(
|
|
|
391
562
|
accountId?: string | null,
|
|
392
563
|
now = Date.now(),
|
|
393
564
|
nativeFallbackOnly = false,
|
|
565
|
+
accountUsabilityOptions?: CodexAccountUsabilityOptions,
|
|
566
|
+
poolAccountPreview?: SubagentPoolAccountPreview,
|
|
567
|
+
modelEligibleAccountIdsForModel?: SubagentModelEligibleAccountIds,
|
|
568
|
+
resolvedFallbackChain?: readonly string[] | null,
|
|
394
569
|
): { from?: string; to?: string; skipped?: string[] } | null {
|
|
395
570
|
if (!isThreadSpawnRequest(headers)) return null;
|
|
396
|
-
const
|
|
397
|
-
|
|
398
|
-
|
|
571
|
+
const fallbackChain = resolvedFallbackChain === undefined
|
|
572
|
+
? resolveSubagentFallbackChain(parsed, config)
|
|
573
|
+
: resolvedFallbackChain;
|
|
574
|
+
if (!fallbackChain) return null;
|
|
399
575
|
const selection = selectAvailableSubagentModel(
|
|
400
576
|
parsed.modelId,
|
|
401
577
|
config,
|
|
402
|
-
|
|
578
|
+
[],
|
|
403
579
|
accountId,
|
|
404
580
|
now,
|
|
405
581
|
nativeFallbackOnly,
|
|
582
|
+
accountUsabilityOptions,
|
|
583
|
+
[],
|
|
584
|
+
poolAccountPreview,
|
|
585
|
+
modelEligibleAccountIdsForModel,
|
|
586
|
+
fallbackChain,
|
|
406
587
|
);
|
|
407
588
|
if (!selection.rewritten) return selection.skipped.length > 0
|
|
408
589
|
? { from: parsed.modelId, to: parsed.modelId, skipped: selection.skipped }
|
|
@@ -412,6 +593,37 @@ export function applySubagentModelFallback(
|
|
|
412
593
|
return { from, to: selection.model, skipped: selection.skipped };
|
|
413
594
|
}
|
|
414
595
|
|
|
596
|
+
/** Resolve the effective fallback chain once for one logical spawn request. */
|
|
597
|
+
export function resolveSubagentFallbackChain(
|
|
598
|
+
parsed: OcxParsedRequest,
|
|
599
|
+
config: OcxConfig,
|
|
600
|
+
): readonly string[] | null {
|
|
601
|
+
const tomlRoleFallback = resolveAgentModelFallbackForPrimary(
|
|
602
|
+
parsed.modelId,
|
|
603
|
+
getCodexHome(),
|
|
604
|
+
config.codexAccountNamespaces,
|
|
605
|
+
);
|
|
606
|
+
// Config-keyed chains are the supported per-role home (#1190); TOML `model_fallback`
|
|
607
|
+
// stays readable for backwards compatibility with homes written before Codex 0.146.
|
|
608
|
+
const configuredFallback = resolveConfiguredModelFallbackForPrimary(parsed.modelId, config);
|
|
609
|
+
const globalFallback = config.subagentModelFallback ?? [];
|
|
610
|
+
if (globalFallback.length === 0 && configuredFallback.length === 0 && tomlRoleFallback.length === 0) return null;
|
|
611
|
+
return normalizedChain(parsed.modelId, config, configuredFallback, tomlRoleFallback);
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
/** Whether the effective fallback chain crosses an account-gated native Pool model. */
|
|
615
|
+
export function subagentFallbackNeedsModelEntitlements(
|
|
616
|
+
fallbackChain: readonly string[] | null,
|
|
617
|
+
config: OcxConfig,
|
|
618
|
+
): boolean {
|
|
619
|
+
return fallbackChain?.some((candidate) => {
|
|
620
|
+
const route = tryRouteFallbackModel(config, candidate);
|
|
621
|
+
return !!route
|
|
622
|
+
&& isPoolCodexRoute(route)
|
|
623
|
+
&& ACCOUNT_GATED_NATIVE_OPENAI_MODELS.has(route.modelId);
|
|
624
|
+
}) === true;
|
|
625
|
+
}
|
|
626
|
+
|
|
415
627
|
export function subagentFallbackGuidanceText(config: OcxConfig): string {
|
|
416
628
|
const chain = config.subagentModelFallback ?? [];
|
|
417
629
|
if (chain.length === 0) return "";
|
|
@@ -419,33 +631,192 @@ export function subagentFallbackGuidanceText(config: OcxConfig): string {
|
|
|
419
631
|
return ` Subagent model fallback chain (priority order): ${quoted}. When the primary model is quota-exhausted, opencodex rewrites thread_spawn requests to the next available model automatically.`;
|
|
420
632
|
}
|
|
421
633
|
|
|
422
|
-
const
|
|
634
|
+
const TOML_MODEL_FALLBACK_KEY = /^\s*(?:model_fallback|"model_fallback"|'model_fallback')\s*=/;
|
|
635
|
+
|
|
636
|
+
type TomlModelFallbackField = { present: false; value: null } | { present: true; value: string[] | null };
|
|
423
637
|
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
638
|
+
type TomlScanState = {
|
|
639
|
+
inMultilineString: '"""' | "'''" | null;
|
|
640
|
+
arrayDepth: number;
|
|
641
|
+
};
|
|
642
|
+
|
|
643
|
+
/** Track TOML strings, comments, and array brackets on one line. */
|
|
644
|
+
function scanTomlLine(line: string, state: TomlScanState): void {
|
|
645
|
+
let i = 0;
|
|
646
|
+
while (i < line.length) {
|
|
647
|
+
const ch = line[i]!;
|
|
648
|
+
if (ch === "#") return;
|
|
649
|
+
if (ch === '"' || ch === "'") {
|
|
650
|
+
const delimiter = ch.repeat(3);
|
|
651
|
+
if (line.startsWith(delimiter, i)) {
|
|
652
|
+
const end = findTomlMultilineStringEnd(line, i + 3, ch);
|
|
653
|
+
if (end === -1) {
|
|
654
|
+
state.inMultilineString = delimiter as '"""' | "'''";
|
|
655
|
+
return;
|
|
656
|
+
}
|
|
657
|
+
i = end + 3;
|
|
658
|
+
continue;
|
|
659
|
+
}
|
|
660
|
+
i++;
|
|
661
|
+
while (i < line.length) {
|
|
662
|
+
if (ch === '"' && line[i] === "\\") {
|
|
663
|
+
i += 2;
|
|
664
|
+
continue;
|
|
665
|
+
}
|
|
666
|
+
if (line[i] === ch) break;
|
|
667
|
+
i++;
|
|
668
|
+
}
|
|
669
|
+
i++;
|
|
670
|
+
continue;
|
|
671
|
+
}
|
|
672
|
+
if (ch === "[") state.arrayDepth++;
|
|
673
|
+
else if (ch === "]") state.arrayDepth = Math.max(0, state.arrayDepth - 1);
|
|
674
|
+
i++;
|
|
675
|
+
}
|
|
427
676
|
}
|
|
428
677
|
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
678
|
+
/** Parse a TOML string starting at `start`; returns the decoded value and end offset. */
|
|
679
|
+
function parseTomlStringAt(text: string, start: number): { value: string; end: number } | null {
|
|
680
|
+
const quote = text[start]!;
|
|
681
|
+
const delimiter = quote.repeat(3);
|
|
682
|
+
if (text.startsWith(delimiter, start)) {
|
|
683
|
+
const end = findTomlMultilineStringEnd(text, start + 3, quote);
|
|
684
|
+
if (end === -1) return null;
|
|
685
|
+
let value = text.slice(start + 3, end).trim();
|
|
686
|
+
if (quote === '"') value = value.replace(/\\"/g, '"').replace(/\\\\/g, "\\");
|
|
687
|
+
return { value, end: end + 3 };
|
|
688
|
+
}
|
|
689
|
+
let i = start + 1;
|
|
690
|
+
let value = "";
|
|
691
|
+
while (i < text.length) {
|
|
692
|
+
const ch = text[i]!;
|
|
693
|
+
if (quote === '"' && ch === "\\") {
|
|
694
|
+
const next = text[i + 1];
|
|
695
|
+
if (next === '"' || next === "\\") {
|
|
696
|
+
value += next;
|
|
697
|
+
i += 2;
|
|
698
|
+
continue;
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
if (ch === quote) return { value, end: i + 1 };
|
|
702
|
+
value += ch;
|
|
703
|
+
i++;
|
|
704
|
+
}
|
|
705
|
+
return null;
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
function findTomlMultilineStringEnd(text: string, from: number, quote: string): number {
|
|
709
|
+
const delimiter = quote.repeat(3);
|
|
710
|
+
let index = text.indexOf(delimiter, from);
|
|
711
|
+
while (index !== -1) {
|
|
712
|
+
if (quote === "'") return index;
|
|
713
|
+
let backslashes = 0;
|
|
714
|
+
for (let j = index - 1; j >= 0 && text[j] === "\\"; j--) backslashes += 1;
|
|
715
|
+
if (backslashes % 2 === 0) return index;
|
|
716
|
+
index = text.indexOf(delimiter, index + 1);
|
|
717
|
+
}
|
|
718
|
+
return -1;
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
/** After an array close, only horizontal whitespace, an inline comment, or the line end is valid. */
|
|
722
|
+
function isValidTomlArrayTail(text: string, from: number): boolean {
|
|
723
|
+
let i = from;
|
|
724
|
+
while (i < text.length && (text[i] === " " || text[i] === "\t")) i += 1;
|
|
725
|
+
if (i >= text.length || text[i] === "\n" || text[i] === "\r") return true;
|
|
726
|
+
if (text[i] === "#") {
|
|
727
|
+
while (i < text.length && text[i] !== "\n") i += 1;
|
|
728
|
+
return true;
|
|
729
|
+
}
|
|
730
|
+
return false;
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
/** Parse a TOML string-array value; null when the value is not a well-formed array of strings. */
|
|
734
|
+
function parseTomlStringArrayValue(text: string): string[] | null {
|
|
735
|
+
const values: string[] = [];
|
|
736
|
+
let i = 0;
|
|
737
|
+
const skipIgnored = () => {
|
|
738
|
+
while (i < text.length) {
|
|
739
|
+
const ch = text[i]!;
|
|
740
|
+
if (ch === "#") {
|
|
741
|
+
while (i < text.length && text[i] !== "\n") i += 1;
|
|
742
|
+
continue;
|
|
743
|
+
}
|
|
744
|
+
if (/\s/.test(ch)) {
|
|
745
|
+
i += 1;
|
|
746
|
+
continue;
|
|
747
|
+
}
|
|
748
|
+
break;
|
|
749
|
+
}
|
|
750
|
+
};
|
|
751
|
+
skipIgnored();
|
|
752
|
+
if (text[i] !== "[") return null;
|
|
753
|
+
i += 1;
|
|
754
|
+
let expectValue = true;
|
|
755
|
+
for (;;) {
|
|
756
|
+
skipIgnored();
|
|
757
|
+
if (i >= text.length) return null;
|
|
758
|
+
const ch = text[i]!;
|
|
759
|
+
if (ch === "]") {
|
|
760
|
+
if (!isValidTomlArrayTail(text, i + 1)) return null;
|
|
761
|
+
return values;
|
|
762
|
+
}
|
|
763
|
+
if (ch === ",") {
|
|
764
|
+
if (expectValue) return null; // leading or doubled comma
|
|
765
|
+
expectValue = true;
|
|
766
|
+
i += 1;
|
|
767
|
+
continue;
|
|
768
|
+
}
|
|
769
|
+
if (ch === '"' || ch === "'") {
|
|
770
|
+
if (!expectValue) return null; // adjacent strings must be comma-separated
|
|
771
|
+
const parsed = parseTomlStringAt(text, i);
|
|
772
|
+
if (!parsed) return null;
|
|
773
|
+
values.push(parsed.value);
|
|
774
|
+
i = parsed.end;
|
|
775
|
+
expectValue = false;
|
|
776
|
+
continue;
|
|
777
|
+
}
|
|
778
|
+
return null;
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
/**
|
|
783
|
+
* TOML-aware, presence-aware parse of the `model_fallback` field. Quoted keys
|
|
784
|
+
* are recognized, and text inside strings (including multiline strings) is
|
|
785
|
+
* never treated as a key. `present` is true whenever the key exists, even when
|
|
786
|
+
* the value is not a readable string array; `value` is null in that case.
|
|
787
|
+
*/
|
|
788
|
+
function parseTomlModelFallbackField(content: string): TomlModelFallbackField {
|
|
789
|
+
const lines = content.split(/\r?\n/);
|
|
790
|
+
const state: TomlScanState = { inMultilineString: null, arrayDepth: 0 };
|
|
791
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
792
|
+
const line = lines[i]!;
|
|
793
|
+
if (state.inMultilineString) {
|
|
794
|
+
const end = findTomlMultilineStringEnd(line, 0, state.inMultilineString[0]!);
|
|
795
|
+
if (end === -1) continue;
|
|
796
|
+
state.inMultilineString = null;
|
|
797
|
+
scanTomlLine(line.slice(end + 3), state);
|
|
798
|
+
continue;
|
|
799
|
+
}
|
|
800
|
+
if (state.arrayDepth === 0) {
|
|
801
|
+
const key = line.match(TOML_MODEL_FALLBACK_KEY);
|
|
802
|
+
if (key) {
|
|
803
|
+
const rest = `${line.slice(key[0].length)}\n${lines.slice(i + 1).join("\n")}`;
|
|
804
|
+
return { present: true, value: parseTomlStringArrayValue(rest) };
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
scanTomlLine(line, state);
|
|
808
|
+
}
|
|
809
|
+
return { present: false, value: null };
|
|
433
810
|
}
|
|
434
811
|
|
|
435
812
|
export function readAgentModelFallback(filePath: string): string[] | null {
|
|
436
813
|
try {
|
|
437
814
|
const content = readFileSync(filePath, "utf8");
|
|
438
|
-
const
|
|
439
|
-
|
|
440
|
-
for (const line of content.split(/\r?\n/)) {
|
|
441
|
-
const match = line.match(TOML_STRING_ARRAY);
|
|
442
|
-
if (!match) continue;
|
|
443
|
-
return parseTomlStringArray(match[2] ?? "");
|
|
444
|
-
}
|
|
815
|
+
const parsed = parseTomlModelFallbackField(content);
|
|
816
|
+
return parsed.present ? parsed.value : null;
|
|
445
817
|
} catch {
|
|
446
818
|
return null;
|
|
447
819
|
}
|
|
448
|
-
return null;
|
|
449
820
|
}
|
|
450
821
|
|
|
451
822
|
export function readCodexAgentModelFallback(role: string, codexHome = CODEX_HOME): string[] {
|
|
@@ -454,6 +825,29 @@ export function readCodexAgentModelFallback(role: string, codexHome = CODEX_HOME
|
|
|
454
825
|
return readAgentModelFallback(file) ?? [];
|
|
455
826
|
}
|
|
456
827
|
|
|
828
|
+
/**
|
|
829
|
+
* True when the role TOML carries a `model_fallback` key, even an empty array.
|
|
830
|
+
* Presence is what matters for the doctor scan: Codex >= 0.146 rejects the
|
|
831
|
+
* field as unknown and skips the whole role regardless of its value. Uses the
|
|
832
|
+
* same TOML-aware parse as the fallback-reading path, so quoted keys count and
|
|
833
|
+
* text inside string literals does not.
|
|
834
|
+
*/
|
|
835
|
+
export function hasCodexAgentModelFallbackField(role: string, codexHome = CODEX_HOME): boolean {
|
|
836
|
+
const file = join(codexHome, "agents", `${role}.toml`);
|
|
837
|
+
if (!existsSync(file)) return false;
|
|
838
|
+
try {
|
|
839
|
+
const content = readFileSync(file, "utf8");
|
|
840
|
+
return parseTomlModelFallbackField(content).present;
|
|
841
|
+
} catch {
|
|
842
|
+
return false;
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
/** Roles whose TOML still carries `model_fallback`, including empty arrays. */
|
|
847
|
+
export function scanCodexAgentRolesWithTomlModelFallback(codexHome = CODEX_HOME): string[] {
|
|
848
|
+
return listCodexAgentRoles(codexHome).filter(role => hasCodexAgentModelFallbackField(role, codexHome));
|
|
849
|
+
}
|
|
850
|
+
|
|
457
851
|
export function listCodexAgentRoles(codexHome = CODEX_HOME): string[] {
|
|
458
852
|
const dir = join(codexHome, "agents");
|
|
459
853
|
if (!existsSync(dir)) return [];
|