@iislee/opencodex 2.11.0 → 2.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS_INSTALL.md +109 -0
- package/README.md +114 -19
- package/bin/ocx.mjs +164 -36
- package/bin/package-main.mjs +1 -1
- package/gui/dist/assets/index-BF38heuV.js +104 -0
- package/gui/dist/assets/index-DMiI18Kv.css +1 -0
- package/gui/dist/index.html +2 -2
- package/gui/dist/provider-icons/alibaba-color.svg +1 -1
- package/gui/dist/provider-icons/antigravity-color.svg +1 -1
- package/gui/dist/provider-icons/claude-color.svg +1 -1
- package/gui/dist/provider-icons/cline-color.svg +16 -0
- package/gui/dist/provider-icons/cloudflare-ai-gateway-color.svg +1 -1
- package/gui/dist/provider-icons/commandcode-color.svg +1 -0
- package/gui/dist/provider-icons/copilot-color.svg +1 -1
- package/gui/dist/provider-icons/cursor-color.svg +1 -1
- package/gui/dist/provider-icons/deepseek-color.svg +1 -1
- package/gui/dist/provider-icons/firepass-color.svg +1 -1
- package/gui/dist/provider-icons/fireworks-color.svg +1 -1
- package/gui/dist/provider-icons/gemini-color.svg +1 -1
- package/gui/dist/provider-icons/github-copilot-color.svg +1 -1
- package/gui/dist/provider-icons/gitlab-duo-color.svg +1 -1
- package/gui/dist/provider-icons/grok.svg +1 -1
- package/gui/dist/provider-icons/groq-color.svg +1 -1
- package/gui/dist/provider-icons/huggingface-color.svg +1 -1
- package/gui/dist/provider-icons/kimi-color.svg +1 -1
- package/gui/dist/provider-icons/kiro-color.svg +2 -2
- package/gui/dist/provider-icons/lm-studio-color.svg +1 -1
- package/gui/dist/provider-icons/mistral-color.svg +1 -1
- package/gui/dist/provider-icons/moonshot-color.svg +1 -1
- package/gui/dist/provider-icons/nvidia-color.svg +1 -1
- package/gui/dist/provider-icons/ollama-color.svg +1 -1
- package/gui/dist/provider-icons/openai.svg +1 -1
- package/gui/dist/provider-icons/opencode.svg +2 -1
- package/gui/dist/provider-icons/openrouter-color.svg +1 -1
- package/gui/dist/provider-icons/pi.svg +2 -2
- package/gui/dist/provider-icons/qianfan-color.svg +1 -1
- package/gui/dist/provider-icons/qwen-portal-color.svg +1 -1
- package/gui/dist/provider-icons/vercel-ai-gateway-color.svg +1 -1
- package/gui/dist/provider-icons/vllm-color.svg +1 -1
- package/gui/dist/provider-icons/xiaomi-color.svg +1 -1
- package/package.json +19 -10
- package/src/adapters/anthropic-output-schema.ts +137 -0
- package/src/adapters/anthropic.ts +376 -52
- package/src/adapters/base.ts +54 -7
- package/src/adapters/client-fingerprint.ts +18 -12
- package/src/adapters/cline-pass-deepseek-v4-tool-replay.ts +69 -0
- package/src/adapters/command-code.ts +601 -0
- package/src/adapters/cursor/checkpoint-store.ts +303 -0
- package/src/adapters/cursor/cursor-errors.ts +129 -6
- package/src/adapters/cursor/discovery.ts +61 -4
- package/src/adapters/cursor/effort-map.ts +27 -3
- package/src/adapters/cursor/framing.ts +39 -0
- package/src/adapters/cursor/h2-pool.ts +123 -0
- package/src/adapters/cursor/http1-bidi.ts +361 -0
- package/src/adapters/cursor/images.ts +704 -0
- package/src/adapters/cursor/live-models.ts +174 -58
- package/src/adapters/cursor/live-transport.ts +609 -170
- package/src/adapters/cursor/native-exec-common.ts +23 -2
- package/src/adapters/cursor/native-exec-fs.ts +10 -7
- package/src/adapters/cursor/native-exec-network.ts +1 -1
- package/src/adapters/cursor/native-exec-shell.ts +0 -1
- package/src/adapters/cursor/native-exec.ts +101 -14
- package/src/adapters/cursor/protobuf-events.ts +829 -11
- package/src/adapters/cursor/protobuf-request.ts +383 -65
- package/src/adapters/cursor/request-builder.ts +237 -31
- package/src/adapters/cursor/tool-definitions.ts +242 -12
- package/src/adapters/cursor/tool-result-normalize.ts +92 -0
- package/src/adapters/cursor/transport.ts +22 -0
- package/src/adapters/cursor/types.ts +28 -1
- package/src/adapters/cursor.ts +133 -7
- package/src/adapters/google-antigravity-replay.ts +558 -34
- package/src/adapters/google-antigravity-wire.ts +43 -10
- package/src/adapters/google-http.ts +38 -10
- package/src/adapters/google-tool-schema.ts +80 -15
- package/src/adapters/google-truncation.ts +11 -0
- package/src/adapters/google.ts +618 -74
- package/src/adapters/identity.ts +39 -6
- package/src/adapters/kiro-errors.ts +11 -0
- package/src/adapters/kiro-events.ts +19 -1
- package/src/adapters/kiro-thinking.ts +18 -2
- package/src/adapters/kiro-tools.ts +10 -1
- package/src/adapters/kiro.ts +92 -54
- package/src/adapters/mimo-free.ts +17 -0
- package/src/adapters/openai-chat-url.ts +11 -0
- package/src/adapters/openai-chat.ts +1311 -336
- package/src/adapters/openai-responses-url.ts +16 -0
- package/src/adapters/openai-responses.ts +830 -56
- package/src/adapters/registry.ts +175 -0
- package/src/adapters/responses-tool-schema.ts +67 -0
- package/src/adapters/tool-call-id.ts +119 -0
- package/src/adapters/tool-catalog-nudge.ts +104 -21
- package/src/adapters/xai-web-search.ts +185 -0
- package/src/bridge.ts +428 -61
- package/src/chat/inbound.ts +43 -19
- package/src/chat/outbound.ts +82 -26
- package/src/claude/agents-inject.ts +32 -9
- package/src/claude/context-windows.ts +21 -5
- package/src/claude/desktop-3p.ts +243 -9
- package/src/claude/gateway-cache.ts +41 -4
- package/src/claude/inbound.ts +72 -3
- package/src/claude/model-info.ts +38 -15
- package/src/claude/outbound.ts +70 -16
- package/src/cli/account-api.ts +35 -1
- package/src/cli/account-auth.ts +33 -6
- package/src/cli/account-catalog-refresh.ts +14 -0
- package/src/cli/account-extended.ts +389 -2
- package/src/cli/account-main.ts +317 -0
- package/src/cli/account.ts +30 -6
- package/src/cli/agent.ts +45 -1
- package/src/cli/claude-agent-startup-sync.ts +73 -0
- package/src/cli/claude-desktop.ts +29 -4
- package/src/cli/claude.ts +103 -34
- package/src/cli/codex-log-guard-doctor.ts +103 -0
- package/src/cli/codex-shim-autorestore.ts +2 -0
- package/src/cli/codex-shim-readiness.ts +76 -0
- package/src/cli/combo.ts +8 -0
- package/src/cli/config-command.ts +74 -10
- package/src/cli/dispatch.ts +593 -0
- package/src/cli/doctor.ts +315 -43
- package/src/cli/ensure-desired-integrations.ts +152 -0
- package/src/cli/export-command.ts +46 -20
- package/src/cli/help.ts +20 -271
- package/src/cli/index.ts +378 -519
- package/src/cli/init.ts +4 -17
- package/src/cli/integrations.ts +120 -2
- package/src/cli/lab.ts +607 -0
- package/src/cli/launcher-context.ts +77 -0
- package/src/cli/minimax.ts +497 -0
- package/src/cli/models-runtime.ts +35 -2
- package/src/cli/models.ts +100 -14
- package/src/cli/observe.ts +92 -3
- package/src/cli/opencode.ts +4 -2
- package/src/cli/provider-runtime.ts +18 -1
- package/src/cli/provider.ts +24 -3
- package/src/cli/ready.ts +301 -0
- package/src/cli/registry.ts +437 -0
- package/src/cli/root.ts +86 -0
- package/src/cli/route-policy.ts +92 -0
- package/src/cli/runtime-api.ts +6 -3
- package/src/cli/star-prompt.ts +71 -15
- package/src/cli/status.ts +10 -3
- package/src/cli/system-restart-client.ts +146 -0
- package/src/cli/tray-proxy.ts +153 -6
- package/src/cli/v2.ts +105 -10
- package/src/cli.ts +1 -1
- package/src/clients/config-export.ts +1358 -21
- package/src/codex/account-label.ts +14 -1
- package/src/codex/account-lifecycle.ts +130 -13
- package/src/codex/account-namespaces.ts +49 -3
- package/src/codex/account-priority.ts +83 -0
- package/src/codex/account-store.ts +29 -2
- package/src/codex/account-usability.ts +25 -2
- package/src/codex/admission.ts +256 -0
- package/src/codex/affinity-debug.ts +162 -0
- package/src/codex/app-server-processes.ts +493 -106
- package/src/codex/app-server-restart-service.ts +232 -0
- package/src/codex/auth-api.ts +849 -242
- package/src/codex/auth-collision.ts +5 -3
- package/src/codex/auth-context.ts +345 -32
- package/src/codex/autostart-health.ts +8 -1
- package/src/codex/catalog/account-models.ts +67 -0
- package/src/codex/catalog/aggregation.ts +68 -10
- package/src/codex/catalog/bundled.ts +331 -33
- package/src/codex/catalog/effort.ts +121 -30
- package/src/codex/catalog/filesystem-evidence.ts +302 -0
- package/src/codex/catalog/kinds.ts +2 -0
- package/src/codex/catalog/metadata.ts +529 -45
- package/src/codex/catalog/native-models.ts +72 -0
- package/src/codex/catalog/parsing.ts +224 -30
- package/src/codex/catalog/provider-fetch.ts +1460 -134
- package/src/codex/catalog/sync.ts +1449 -186
- package/src/codex/catalog-admission.ts +199 -0
- package/src/codex/catalog-refresh-status.ts +105 -0
- package/src/codex/catalog-write-serialization.ts +242 -0
- package/src/codex/catalog.ts +6 -3
- package/src/codex/codex-write-lock.ts +384 -0
- package/src/codex/convergence-types.ts +614 -0
- package/src/codex/convergence.ts +651 -0
- package/src/codex/coordinator-doctor.ts +332 -0
- package/src/codex/custom-model-catalog-migration.ts +176 -0
- package/src/codex/desired-state.ts +230 -0
- package/src/codex/features.ts +636 -39
- package/src/codex/generation.ts +202 -0
- package/src/codex/history-job.ts +407 -0
- package/src/codex/history-lock.ts +242 -0
- package/src/codex/history-migration-guardian.ts +26 -20
- package/src/codex/history-provider.ts +231 -28
- package/src/codex/history-transition.ts +105 -0
- package/src/codex/history-worker.ts +220 -0
- package/src/codex/inject-coordination.ts +290 -0
- package/src/codex/inject.ts +1073 -152
- package/src/codex/injected-marker.ts +37 -3
- package/src/codex/integration-record.ts +266 -0
- package/src/codex/internal/catalog-writer.ts +203 -0
- package/src/codex/internal/history-writer.ts +80 -0
- package/src/codex/journal.ts +66 -4
- package/src/codex/log-guard/inspect.ts +506 -0
- package/src/codex/log-guard/lock.ts +150 -0
- package/src/codex/log-guard/maintenance.ts +403 -0
- package/src/codex/log-guard/path-safety.ts +88 -0
- package/src/codex/log-guard/policy.ts +44 -0
- package/src/codex/log-guard/processes.ts +205 -0
- package/src/codex/log-guard/protection.ts +489 -0
- package/src/codex/log-guard/sqlite-errors.ts +9 -0
- package/src/codex/main-account-cache.ts +24 -0
- package/src/codex/main-account.ts +29 -1
- package/src/codex/management-convergence.ts +167 -0
- package/src/codex/model-cache.ts +56 -10
- package/src/codex/model-entitlements.ts +353 -0
- package/src/codex/native-main-admission.ts +47 -0
- package/src/codex/native-main-auth-temp.ts +187 -0
- package/src/codex/native-main-claim.ts +178 -0
- package/src/codex/native-main-lock-file.ts +162 -0
- package/src/codex/native-main-owner.ts +329 -0
- package/src/codex/native-profile-api.ts +247 -0
- package/src/codex/native-profile-manager.ts +1531 -0
- package/src/codex/native-profile-processes.ts +121 -0
- package/src/codex/native-profile-recovery.ts +99 -0
- package/src/codex/native-profile-stage-store.ts +387 -0
- package/src/codex/native-profile-startup.ts +492 -0
- package/src/codex/native-profile-store.ts +855 -0
- package/src/codex/native-profile-types.ts +120 -0
- package/src/codex/native-residue.ts +682 -0
- package/src/codex/paths.ts +80 -1
- package/src/codex/plan-from-token.ts +140 -0
- package/src/codex/plan.ts +40 -0
- package/src/codex/plugins-doctor.ts +1 -1
- package/src/codex/pool-rotation.ts +74 -4
- package/src/codex/project-config-warnings.ts +20 -6
- package/src/codex/prompt-journal.ts +352 -0
- package/src/codex/prompt-layers.ts +967 -0
- package/src/codex/prompt-lock.ts +143 -0
- package/src/codex/quota-rejection.ts +298 -0
- package/src/codex/quota.ts +175 -13
- package/src/codex/refresh.ts +11 -2
- package/src/codex/reset-credit-recovery.ts +1044 -0
- package/src/codex/routing.ts +505 -94
- package/src/codex/runtime.ts +159 -38
- package/src/codex/shim.ts +1009 -28
- package/src/codex/subagent-model-fallback.ts +350 -35
- package/src/codex/sync.ts +191 -2
- package/src/codex/transition-state.ts +612 -0
- package/src/codex/upstream-host-health.ts +368 -0
- package/src/codex/user-identity.ts +557 -0
- package/src/codex/warmup.ts +187 -81
- package/src/codex/write-coordination.ts +114 -0
- package/src/combos/failover.ts +20 -0
- package/src/combos/index.ts +4 -0
- package/src/combos/request.ts +32 -0
- package/src/combos/types.ts +81 -9
- package/src/config/provider-name.ts +24 -0
- package/src/config.ts +1762 -140
- package/src/generated/compatibility-version.json +3164 -0
- package/src/generated/{jawcode-model-metadata.ts → model-metadata.ts} +19 -17
- package/src/grok/inject.ts +16 -5
- package/src/grok/inspect.ts +45 -0
- package/src/grok/sync.ts +2 -2
- package/src/images/loop.ts +152 -29
- package/src/images/plan.ts +23 -13
- package/src/integrations/config-io.ts +269 -0
- package/src/integrations/journal.ts +315 -0
- package/src/integrations/merge.ts +135 -0
- package/src/integrations/mutation-flight.ts +71 -0
- package/src/integrations/native/ownership-preflight.ts +202 -0
- package/src/integrations/omp-yaml-source.ts +358 -0
- package/src/integrations/owned-refresh.ts +74 -0
- package/src/integrations/ownership.ts +111 -0
- package/src/integrations/registry.ts +159 -0
- package/src/integrations/serialize.ts +314 -0
- package/src/integrations/state.ts +361 -0
- package/src/integrations/store.ts +103 -0
- package/src/integrations/writer-lock.ts +98 -0
- package/src/integrations/writer.ts +691 -0
- package/src/lab/artifacts/sanitize.ts +586 -0
- package/src/lab/artifacts/secure-fs.ts +475 -0
- package/src/lab/artifacts/store.ts +310 -0
- package/src/lab/automation/budgets.ts +78 -0
- package/src/lab/automation/config-persistence.ts +256 -0
- package/src/lab/automation/constants.ts +39 -0
- package/src/lab/automation/cooldown.ts +103 -0
- package/src/lab/automation/dispatch.ts +211 -0
- package/src/lab/automation/index.ts +13 -0
- package/src/lab/automation/orchestrator.ts +499 -0
- package/src/lab/automation/persistence.ts +512 -0
- package/src/lab/automation/planner.ts +371 -0
- package/src/lab/automation/policy.ts +136 -0
- package/src/lab/automation/queue.ts +191 -0
- package/src/lab/automation/recovery.ts +24 -0
- package/src/lab/automation/route-context.ts +21 -0
- package/src/lab/automation/run-key.ts +44 -0
- package/src/lab/automation/runs-query.ts +34 -0
- package/src/lab/automation/types.ts +160 -0
- package/src/lab/conformance/assertion.ts +325 -0
- package/src/lab/conformance/digest.ts +22 -0
- package/src/lab/conformance/executor.ts +741 -0
- package/src/lab/conformance/fixture-provider.ts +27 -0
- package/src/lab/conformance/fixtures/live-v1-cases.json +175 -0
- package/src/lab/conformance/fixtures/protocol-v1-cases.json +461 -0
- package/src/lab/conformance/harness-budget.ts +47 -0
- package/src/lab/conformance/index.ts +5 -0
- package/src/lab/conformance/jcs.ts +64 -0
- package/src/lab/conformance/json-pointer.ts +39 -0
- package/src/lab/conformance/manifest.ts +180 -0
- package/src/lab/conformance/mcp-stub.ts +179 -0
- package/src/lab/conformance/negative-controls.ts +164 -0
- package/src/lab/conformance/observation.ts +355 -0
- package/src/lab/conformance/runner.ts +68 -0
- package/src/lab/conformance/sse-normalize.ts +59 -0
- package/src/lab/conformance/suite-manifest.ts +78 -0
- package/src/lab/conformance/types.ts +214 -0
- package/src/lab/constants.ts +126 -0
- package/src/lab/digest.ts +64 -0
- package/src/lab/events/errors.ts +9 -0
- package/src/lab/events/limits.ts +117 -0
- package/src/lab/events/types.ts +229 -0
- package/src/lab/events/validate.ts +781 -0
- package/src/lab/fabric/constants.ts +40 -0
- package/src/lab/fabric/executor.ts +492 -0
- package/src/lab/fabric/index.ts +80 -0
- package/src/lab/fabric/manifest.ts +222 -0
- package/src/lab/fabric/observe.ts +489 -0
- package/src/lab/fabric/patch.ts +79 -0
- package/src/lab/fabric/producer-child.ts +139 -0
- package/src/lab/fabric/producer-isolate.ts +276 -0
- package/src/lab/fabric/producer-protocol.ts +61 -0
- package/src/lab/fabric/scratch.ts +439 -0
- package/src/lab/fabric/subject.ts +106 -0
- package/src/lab/fabric/types.ts +134 -0
- package/src/lab/fabric/verifier.ts +98 -0
- package/src/lab/index.ts +54 -0
- package/src/lab/ledger/artifact-refs.ts +127 -0
- package/src/lab/ledger/invalidation.ts +136 -0
- package/src/lab/ledger/purge.ts +310 -0
- package/src/lab/ledger/store.ts +532 -0
- package/src/lab/live/credential-lease.ts +53 -0
- package/src/lab/live/destination.ts +155 -0
- package/src/lab/live/executor.ts +336 -0
- package/src/lab/live/inert-tools.ts +56 -0
- package/src/lab/live/manifest.ts +85 -0
- package/src/lab/live/mcp-loopback.ts +57 -0
- package/src/lab/live/runner.ts +19 -0
- package/src/lab/live/sandbox.ts +61 -0
- package/src/lab/live/suite-manifest.ts +41 -0
- package/src/lab/live/transport.ts +118 -0
- package/src/lab/live/types.ts +197 -0
- package/src/lab/observe/from-conformance.ts +301 -0
- package/src/lab/observe/from-live.ts +117 -0
- package/src/lab/paths.ts +153 -0
- package/src/lab/projection/rebuild.ts +495 -0
- package/src/lab/projection/schema.ts +135 -0
- package/src/lab/projection/verdicts.ts +474 -0
- package/src/lab/projection/verification.ts +412 -0
- package/src/lab/public/bundle.ts +217 -0
- package/src/lab/public/community-authority.ts +175 -0
- package/src/lab/public/community-files.ts +29 -0
- package/src/lab/public/community.ts +479 -0
- package/src/lab/public/file-safety.ts +155 -0
- package/src/lab/public/ids.ts +26 -0
- package/src/lab/public/index.ts +16 -0
- package/src/lab/public/mutation-lock.ts +424 -0
- package/src/lab/public/operator.ts +353 -0
- package/src/lab/public/origin-purge.ts +79 -0
- package/src/lab/public/origin.ts +203 -0
- package/src/lab/public/privacy.ts +143 -0
- package/src/lab/public/private-file.ts +261 -0
- package/src/lab/public/project.ts +124 -0
- package/src/lab/public/purge-test-fault.ts +21 -0
- package/src/lab/public/purge.ts +223 -0
- package/src/lab/public/registry.ts +44 -0
- package/src/lab/public/revocation.ts +252 -0
- package/src/lab/public/signature.ts +243 -0
- package/src/lab/public/storage.ts +105 -0
- package/src/lab/public/strict-json.ts +206 -0
- package/src/lab/public/time.ts +26 -0
- package/src/lab/public/types.ts +172 -0
- package/src/lab/public/validate.ts +391 -0
- package/src/lab/query/catalog.ts +101 -0
- package/src/lab/query/connection.ts +107 -0
- package/src/lab/query/constants.ts +4 -0
- package/src/lab/query/cursor.ts +132 -0
- package/src/lab/query/dto-map.ts +277 -0
- package/src/lab/query/errors.ts +22 -0
- package/src/lab/query/freshness.ts +53 -0
- package/src/lab/query/index.ts +45 -0
- package/src/lab/query/latest-observation.ts +59 -0
- package/src/lab/query/passive-production.ts +159 -0
- package/src/lab/query/queries.ts +444 -0
- package/src/lab/query/types.ts +266 -0
- package/src/lab/subject/behavior-fingerprint.ts +77 -0
- package/src/lab/subject/installation-salt.ts +112 -0
- package/src/lab/subject/protocol-subject.ts +80 -0
- package/src/lab/subject/route-subject.ts +74 -0
- package/src/lib/app-owned-memory-stores.ts +22 -0
- package/src/lib/bounded-body.ts +153 -9
- package/src/lib/bun-runtime.ts +125 -12
- package/src/lib/bun-stream-caps.ts +13 -9
- package/src/lib/codex-restart-contract.ts +120 -0
- package/src/lib/config-ownership.ts +6 -2
- package/src/lib/destination-policy.ts +65 -1
- package/src/lib/errors.ts +44 -2
- package/src/lib/fabric-task-execution-authority.ts +7 -0
- package/src/lib/fabric-task-host.ts +29 -0
- package/src/lib/lab-activation.ts +223 -0
- package/src/lib/lab-live-execution-authority.ts +13 -0
- package/src/lib/lab-live-host.ts +30 -0
- package/src/lib/lab-live-pinned-sender.ts +56 -0
- package/src/lib/lab-live-route-production.ts +130 -0
- package/src/lib/lab-passive-linker-registration.ts +26 -0
- package/src/lib/local-management-attestation.ts +51 -0
- package/src/lib/local-management-capability.ts +100 -0
- package/src/lib/local-provider-reload-contract.ts +100 -0
- package/src/lib/optional-shutdown-hooks.ts +57 -0
- package/src/lib/pinned-http.ts +145 -26
- package/src/lib/process-control.ts +4 -1
- package/src/lib/provider-outbound.ts +49 -9
- package/src/lib/redact.ts +419 -3
- package/src/lib/self-launch-argv.ts +15 -0
- package/src/lib/server-resource-ownership.ts +71 -0
- package/src/lib/shadow-call.ts +35 -4
- package/src/lib/sse-decoder.ts +41 -0
- package/src/lib/state-store-registrations.ts +10 -2
- package/src/lib/system-restart-contract.ts +73 -0
- package/src/lib/token-estimate.ts +19 -2
- package/src/lib/tool-argument-integers.ts +202 -0
- package/src/lib/translator-budget.ts +44 -0
- package/src/lib/upstream-http-version.ts +57 -0
- package/src/lib/upstream-reachability.ts +95 -0
- package/src/lib/upstream-retry.ts +156 -3
- package/src/lib/windows-atomic-replace.ts +155 -0
- package/src/lib/windows-elevation.ts +70 -2
- package/src/lib/windows-secret-acl.ts +409 -69
- package/src/lib/windows-service-wrappers.ts +72 -0
- package/src/lib/windows-text.ts +106 -0
- package/src/lib/windows-user-principal.ts +341 -0
- package/src/lib/winsw.ts +33 -5
- package/src/oauth/account-import/google-antigravity-adapter.ts +74 -0
- package/src/oauth/account-import/index.ts +15 -0
- package/src/oauth/account-import/parser.ts +83 -0
- package/src/oauth/account-import/registry.ts +18 -0
- package/src/oauth/account-import/service.ts +75 -0
- package/src/oauth/account-import/types.ts +91 -0
- package/src/oauth/anthropic.ts +12 -1
- package/src/oauth/callback-server.ts +8 -2
- package/src/oauth/chatgpt.ts +12 -1
- package/src/oauth/command-code.ts +239 -0
- package/src/oauth/cursor.ts +46 -5
- package/src/oauth/google-antigravity.ts +35 -3
- package/src/oauth/health.ts +20 -12
- package/src/oauth/index.ts +398 -66
- package/src/oauth/key-providers.ts +16 -0
- package/src/oauth/kimi.ts +16 -2
- package/src/oauth/kiro.ts +50 -6
- package/src/oauth/local-token-detect.ts +11 -2
- package/src/oauth/log.ts +3 -1
- package/src/oauth/login-cli.ts +88 -28
- package/src/oauth/nous.ts +798 -0
- package/src/oauth/store.ts +119 -21
- package/src/oauth/token-guardian.ts +9 -3
- package/src/pi/models.ts +2 -2
- package/src/providers/alibaba-region-migration.ts +1 -1
- package/src/providers/antigravity-models.ts +521 -31
- package/src/providers/base-url-choices.ts +10 -0
- package/src/providers/codex-capacity.ts +292 -0
- package/src/providers/command-code-efforts.ts +144 -0
- package/src/providers/context-cap.ts +22 -5
- package/src/providers/cursor-pool.ts +72 -0
- package/src/providers/derive.ts +253 -6
- package/src/providers/fastwire.ts +501 -0
- package/src/providers/free-directory.ts +10 -7
- package/src/providers/google-vertex-location.ts +14 -0
- package/src/providers/key-failover.ts +71 -3
- package/src/providers/label.ts +1 -1
- package/src/providers/model-discovery-limits.ts +16 -0
- package/src/providers/model-discovery.ts +115 -22
- package/src/providers/model-rename-migration.ts +255 -0
- package/src/providers/model-rename-startup.ts +28 -0
- package/src/providers/openai-sidecar.ts +72 -4
- package/src/providers/openai-tier-startup.ts +31 -2
- package/src/providers/openai-tiers.ts +119 -4
- package/src/providers/openai-virtual-models.ts +1 -0
- package/src/providers/opencode-zen-rate-limit.ts +102 -0
- package/src/providers/provider-id-rewrite.ts +29 -0
- package/src/providers/quota.ts +1319 -38
- package/src/providers/registry.ts +1429 -111
- package/src/providers/request-pacing.ts +310 -0
- package/src/providers/service-tier.ts +277 -0
- package/src/providers/slug-codec.ts +42 -6
- package/src/providers/static-model-discovery.ts +86 -0
- package/src/providers/xai-responses-opt-in.ts +15 -0
- package/src/providers/xai-transport.ts +11 -4
- package/src/reasoning-effort.ts +49 -1
- package/src/responses/compaction.ts +26 -1
- package/src/responses/custom-tool-compat.ts +266 -0
- package/src/responses/hosted-tool-policy.ts +9 -0
- package/src/responses/namespace-tool-compat.ts +355 -0
- package/src/responses/parser.ts +220 -38
- package/src/responses/provider-continuation.ts +98 -0
- package/src/responses/provider-opaque-metadata.ts +73 -0
- package/src/responses/reasoning-envelope.ts +9 -1
- package/src/responses/reasoning-replay-cache.ts +426 -0
- package/src/responses/schema.ts +7 -1
- package/src/responses/spill-store.ts +75 -10
- package/src/responses/state.ts +565 -27
- package/src/responses/thought-signature-replay.ts +347 -0
- package/src/responses/tool-search-compat.ts +301 -0
- package/src/responses/truncated-stop-reason.ts +60 -0
- package/src/router.ts +366 -30
- package/src/routing/analytics.ts +378 -0
- package/src/routing/capability.ts +244 -0
- package/src/routing/compatibility/assemble.ts +73 -0
- package/src/routing/compatibility/behavior.ts +278 -0
- package/src/routing/compatibility/catalog.ts +99 -0
- package/src/routing/compatibility/endpoint.ts +52 -0
- package/src/routing/compatibility/lab-evidence-provider.ts +130 -0
- package/src/routing/compatibility/policy.ts +181 -0
- package/src/routing/compatibility/provider-slot.ts +56 -0
- package/src/routing/compatibility/reader.ts +110 -0
- package/src/routing/compatibility/subject.ts +191 -0
- package/src/routing/compatibility/types.ts +64 -0
- package/src/routing/compatibility/version.ts +104 -0
- package/src/routing/cost.ts +77 -0
- package/src/routing/evaluator.ts +495 -0
- package/src/routing/health.ts +412 -0
- package/src/routing/history/cursor.ts +43 -0
- package/src/routing/history/indexer.ts +605 -0
- package/src/routing/history/schema.ts +72 -0
- package/src/routing/profile-namespace.ts +15 -0
- package/src/routing/profile.ts +547 -0
- package/src/routing/quota.ts +145 -0
- package/src/routing/request-evidence.ts +45 -0
- package/src/routing/trace.ts +776 -0
- package/src/server/adapter-resolve.ts +2 -29
- package/src/server/auth-cors.ts +267 -46
- package/src/server/background-lifecycle.ts +182 -0
- package/src/server/chat-completions.ts +130 -56
- package/src/server/chat-native-sse.ts +331 -0
- package/src/server/chat-native.ts +426 -0
- package/src/server/claude-messages.ts +159 -43
- package/src/server/direct-local-http.ts +347 -0
- package/src/server/effort-policy.ts +18 -0
- package/src/server/github-copilot-responses-repair.ts +338 -0
- package/src/server/gui-static.ts +39 -10
- package/src/server/images.ts +94 -12
- package/src/server/index.ts +865 -181
- package/src/server/lifecycle.ts +284 -13
- package/src/server/live.ts +136 -17
- package/src/server/local-management-read-client.ts +90 -0
- package/src/server/local-provider-reload-client.ts +137 -0
- package/src/server/management/agent-settings-routes.ts +398 -103
- package/src/server/management/api-key-usage.ts +31 -5
- package/src/server/management/body.ts +6 -0
- package/src/server/management/combo-routes.ts +62 -24
- package/src/server/management/config-routes.ts +464 -51
- package/src/server/management/context.ts +80 -2
- package/src/server/management/integration-routes.ts +498 -0
- package/src/server/management/lab-automation-routes.ts +206 -0
- package/src/server/management/lab-routes.ts +563 -0
- package/src/server/management/logs-usage-routes.ts +101 -32
- package/src/server/management/model-routes.ts +189 -131
- package/src/server/management/model-rows.ts +163 -0
- package/src/server/management/native-integration-routes.ts +769 -0
- package/src/server/management/oauth-account-routes.ts +80 -4
- package/src/server/management/provider-capability-config.ts +48 -0
- package/src/server/management/provider-routes.ts +764 -157
- package/src/server/management/request-history-routes.ts +191 -0
- package/src/server/management/routing-analytics-routes.ts +74 -0
- package/src/server/management/routing-profile-routes.ts +380 -0
- package/src/server/management/shared.ts +27 -11
- package/src/server/management/sidebar-routes.ts +47 -31
- package/src/server/management/storage-log-guard-routes.ts +186 -0
- package/src/server/management/sync-response.ts +69 -0
- package/src/server/management/system-restart.ts +288 -32
- package/src/server/management/system-routes.ts +77 -0
- package/src/server/management/usage-summary-cache.ts +9 -1
- package/src/server/management/vision-sidecar-options.ts +167 -0
- package/src/server/management/web-search-sidecar-options.ts +120 -0
- package/src/server/management-api.ts +115 -14
- package/src/server/management-auth.ts +220 -5
- package/src/server/passive-route-linker.ts +66 -0
- package/src/server/ports.ts +41 -1
- package/src/server/proxy-liveness.ts +132 -5
- package/src/server/readiness.ts +99 -0
- package/src/server/relay-eager.ts +82 -42
- package/src/server/relay.ts +236 -76
- package/src/server/request-decompress.ts +113 -6
- package/src/server/request-log.ts +235 -22
- package/src/server/responses/agent-task-recovery-cache.ts +143 -0
- package/src/server/responses/agent-task-recovery.ts +465 -0
- package/src/server/responses/collaboration.ts +204 -35
- package/src/server/responses/compact.ts +442 -55
- package/src/server/responses/core.ts +2872 -331
- package/src/server/responses/empty-completion-guard.ts +276 -0
- package/src/server/responses/encrypted-payload.ts +62 -39
- package/src/server/responses/fetch-helpers.ts +79 -4
- package/src/server/responses/input-admission.ts +185 -0
- package/src/server/responses/pacing-overload.ts +13 -0
- package/src/server/responses/policy-fallback.ts +178 -0
- package/src/server/responses/responses-field-backfill.ts +251 -0
- package/src/server/responses/terminal-guard.ts +26 -5
- package/src/server/responses/upstream-error.ts +5 -0
- package/src/server/responses/ws-upstream.ts +308 -0
- package/src/server/responses-custom-tool-repair.ts +282 -0
- package/src/server/responses-item-id-repair.ts +54 -6
- package/src/server/responses-json-events.ts +90 -0
- package/src/server/responses-model-rewrite.ts +29 -0
- package/src/server/responses-reasoning-summary-rewrite.ts +178 -0
- package/src/server/responses-snapshot-repair.ts +621 -0
- package/src/server/responses-terminal-repair.ts +342 -0
- package/src/server/responses-tool-search-repair.ts +267 -0
- package/src/server/responses-undeclared-tool-guard.ts +153 -0
- package/src/server/responses.ts +18 -2
- package/src/server/search.ts +78 -13
- package/src/server/sse-frame-buffer.ts +292 -0
- package/src/server/sse-payload-rewrite.ts +110 -22
- package/src/server/startup-action-control.ts +8 -1
- package/src/server/startup-health-cache.ts +19 -1
- package/src/server/system-env.ts +80 -9
- package/src/server/ws-bridge.ts +39 -38
- package/src/service-manager-probe.ts +892 -0
- package/src/service.ts +1111 -90
- package/src/sidecar/auth.ts +92 -0
- package/src/sidecar/candidates.ts +83 -0
- package/src/storage/cleanup.ts +2 -2
- package/src/storage/scanner.ts +1 -1
- package/src/storage/worker-lifecycle.ts +14 -14
- package/src/tray/windows-tray.ps1 +83 -9
- package/src/tray/windows.ts +43 -16
- package/src/types/accounts.ts +37 -0
- package/src/types/config.ts +845 -0
- package/src/types/provider.ts +545 -0
- package/src/types/request.ts +384 -0
- package/src/types/tools.ts +131 -0
- package/src/types/wire.ts +80 -0
- package/src/types.ts +104 -1236
- package/src/update/index.ts +32 -19
- package/src/update/job.ts +442 -67
- package/src/update/notify.ts +12 -6
- package/src/update/npm-cache-preflight.d.mts +47 -0
- package/src/update/npm-cache-preflight.mjs +201 -0
- package/src/update/transactional-install.d.mts +22 -0
- package/src/update/transactional-install.mjs +259 -0
- package/src/usage/cost.ts +0 -0
- package/src/usage/expected-prices.ts +268 -16
- package/src/usage/log.ts +606 -41
- package/src/usage/summary.ts +177 -9
- package/src/usage/user-cost-overlay-reconciler.ts +313 -0
- package/src/usage/user-cost-overlays.ts +314 -0
- package/src/vision/anthropic-describe.ts +10 -6
- package/src/vision/backends.ts +97 -0
- package/src/vision/describe.ts +9 -3
- package/src/vision/eligibility.ts +250 -0
- package/src/vision/index.ts +238 -24
- package/src/vision/reasoning.ts +55 -0
- package/src/vision/routed-describe.ts +175 -0
- package/src/vision/timeout-bounds.ts +9 -0
- package/src/web-search/anthropic-executor.ts +13 -7
- package/src/web-search/backends.ts +108 -0
- package/src/web-search/exa-executor.ts +88 -0
- package/src/web-search/executor.ts +11 -3
- package/src/web-search/gemini-executor.ts +141 -0
- package/src/web-search/index.ts +150 -15
- package/src/web-search/loop.ts +279 -50
- package/src/web-search/parse.ts +125 -30
- package/src/web-search/sources.ts +60 -0
- package/src/web-search/xai-executor.ts +219 -0
- package/gui/dist/assets/index-DTpMHS4F.js +0 -67
- package/gui/dist/assets/index-ZNVDE3C7.css +0 -1
- package/gui/dist/provider-icons/antigravity.svg +0 -1
- package/gui/dist/provider-icons/claude.svg +0 -1
- package/gui/dist/provider-icons/copilot.svg +0 -1
- package/gui/dist/provider-icons/cursor.svg +0 -2
- package/gui/dist/provider-icons/gemini.svg +0 -1
- package/gui/dist/provider-icons/grok-color.svg +0 -1
- package/gui/dist/provider-icons/kiro.svg +0 -14
- package/src/cli/internal-dispatch.ts +0 -20
|
@@ -15,20 +15,38 @@ import { CODEX_HOME, getCodexHome } from "./paths";
|
|
|
15
15
|
import { CODEX_UNKNOWN_USAGE_SCORE, getAccountQuota } from "./quota";
|
|
16
16
|
import {
|
|
17
17
|
canAcquireCodexQuotaProbeLease,
|
|
18
|
+
codexQuotaScopeForModel,
|
|
18
19
|
computeCodexUsageScore,
|
|
20
|
+
getCodexQuotaHealthSnapshot,
|
|
21
|
+
getEffectiveActiveCodexAccountId,
|
|
19
22
|
getPoolAccountPlan,
|
|
20
23
|
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 {
|
|
42
|
+
getUpstreamHostHealth,
|
|
43
|
+
normalizeUpstreamHostCircuitThreshold,
|
|
44
|
+
upstreamHostHealthKey,
|
|
45
|
+
} from "./upstream-host-health";
|
|
30
46
|
export const DEFAULT_SUBAGENT_MODEL_FALLBACK_POLL_MS = 60_000;
|
|
31
47
|
|
|
48
|
+
const CODEX_FORWARD_ORIGIN = new URL(CODEX_FORWARD_BASE_URL).origin.toLowerCase();
|
|
49
|
+
|
|
32
50
|
type SubagentQuotaPrimeFn = (config: OcxConfig, reason: string) => Promise<void>;
|
|
33
51
|
let subagentQuotaPrimeForTests: SubagentQuotaPrimeFn | null = null;
|
|
34
52
|
let quotaPrimeInFlight: Promise<void> | null = null;
|
|
@@ -68,6 +86,13 @@ function isDisabledFallbackModel(model: string, config: OcxConfig): boolean {
|
|
|
68
86
|
const slash = model.indexOf("/");
|
|
69
87
|
const provider = model.slice(0, slash);
|
|
70
88
|
const modelId = model.slice(slash + 1);
|
|
89
|
+
if (codexAccountNamespaceForModel(config.codexAccountNamespaces, model)) {
|
|
90
|
+
return disabled.some(stored =>
|
|
91
|
+
stored === model
|
|
92
|
+
|| stored === modelId
|
|
93
|
+
|| slugEquals(stored, "openai", modelId)
|
|
94
|
+
);
|
|
95
|
+
}
|
|
71
96
|
return disabled.some(stored => stored === model || slugEquals(stored, provider, modelId));
|
|
72
97
|
}
|
|
73
98
|
|
|
@@ -79,13 +104,27 @@ function pollIntervalMs(config: OcxConfig): number {
|
|
|
79
104
|
return configured;
|
|
80
105
|
}
|
|
81
106
|
|
|
82
|
-
function
|
|
107
|
+
function fallbackChainKey(model: string, namespaces: unknown): string {
|
|
108
|
+
const selector = codexAccountNamespaceForModel(namespaces, model);
|
|
109
|
+
if (!selector) return JSON.stringify(["model", model.toLowerCase()]);
|
|
110
|
+
const slash = model.indexOf("/");
|
|
111
|
+
// Selector keys are exact-case account boundaries. Keep that segment distinct while
|
|
112
|
+
// retaining legacy case-insensitive de-duplication for the native model suffix.
|
|
113
|
+
return JSON.stringify(["account", selector, model.slice(slash + 1).toLowerCase()]);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function normalizedChain(
|
|
117
|
+
primary: string,
|
|
118
|
+
config: OcxConfig,
|
|
119
|
+
extra: readonly string[] = [],
|
|
120
|
+
trailing: readonly string[] = [],
|
|
121
|
+
): string[] {
|
|
83
122
|
const chain: string[] = [];
|
|
84
123
|
const seen = new Set<string>();
|
|
85
124
|
const push = (model: string | undefined) => {
|
|
86
125
|
if (!model || model.trim() === "") return;
|
|
87
126
|
const trimmed = model.trim();
|
|
88
|
-
const key = trimmed.
|
|
127
|
+
const key = fallbackChainKey(trimmed, config.codexAccountNamespaces);
|
|
89
128
|
if (seen.has(key)) return;
|
|
90
129
|
seen.add(key);
|
|
91
130
|
chain.push(trimmed);
|
|
@@ -93,6 +132,7 @@ function normalizedChain(primary: string, config: OcxConfig, extra: readonly str
|
|
|
93
132
|
push(primary);
|
|
94
133
|
for (const model of extra) push(model);
|
|
95
134
|
for (const model of config.subagentModelFallback ?? []) push(model);
|
|
135
|
+
for (const model of trailing) push(model);
|
|
96
136
|
return chain;
|
|
97
137
|
}
|
|
98
138
|
|
|
@@ -109,8 +149,14 @@ function quotaThreshold(config: OcxConfig): number {
|
|
|
109
149
|
return threshold > 0 ? threshold : Number.POSITIVE_INFINITY;
|
|
110
150
|
}
|
|
111
151
|
|
|
152
|
+
/**
|
|
153
|
+
* The account routing would actually use, not just the persisted operator
|
|
154
|
+
* selection: round-robin, fill-first, failover, and priority preemption all
|
|
155
|
+
* move the cursor in memory only, so reading the raw field would check quota
|
|
156
|
+
* against an account this request is not going to touch.
|
|
157
|
+
*/
|
|
112
158
|
function activeCodexAccountId(config: OcxConfig): string | null {
|
|
113
|
-
return config
|
|
159
|
+
return getEffectiveActiveCodexAccountId(config) ?? null;
|
|
114
160
|
}
|
|
115
161
|
|
|
116
162
|
/**
|
|
@@ -127,9 +173,18 @@ function resolvePoolFallbackAccountId(
|
|
|
127
173
|
return activeCodexAccountId(config);
|
|
128
174
|
}
|
|
129
175
|
|
|
176
|
+
function resolveRouteFallbackAccountId(
|
|
177
|
+
route: RouteResult | null,
|
|
178
|
+
config: OcxConfig,
|
|
179
|
+
accountId?: string | null,
|
|
180
|
+
): string | null {
|
|
181
|
+
return route?.codexAccountId ?? resolvePoolFallbackAccountId(config, accountId);
|
|
182
|
+
}
|
|
183
|
+
|
|
130
184
|
function isRoutableFallbackModel(model: string, config: OcxConfig): boolean {
|
|
131
185
|
const slash = model.indexOf("/");
|
|
132
186
|
if (slash > 0) {
|
|
187
|
+
if (codexAccountNamespaceForModel(config.codexAccountNamespaces, model)) return true;
|
|
133
188
|
const providerName = model.slice(0, slash);
|
|
134
189
|
if (!hasOwnProvider(config.providers, providerName)) {
|
|
135
190
|
// Allow well-known "vendor/model" ids (e.g. anthropic/claude-*) to flow as
|
|
@@ -150,7 +205,7 @@ export function isNativeModelQuotaExhausted(
|
|
|
150
205
|
): boolean {
|
|
151
206
|
const route = tryRouteFallbackModel(config, model);
|
|
152
207
|
if (!route || !isPoolCodexRoute(route)) return false;
|
|
153
|
-
const resolvedAccountId =
|
|
208
|
+
const resolvedAccountId = resolveRouteFallbackAccountId(route, config, accountId);
|
|
154
209
|
if (!resolvedAccountId) return false;
|
|
155
210
|
const quota = getAccountQuota(resolvedAccountId);
|
|
156
211
|
const usage = computeCodexUsageScore(quota, getPoolAccountPlan(config, resolvedAccountId));
|
|
@@ -167,7 +222,11 @@ export function isModelHealthBlocked(
|
|
|
167
222
|
const route = tryRouteFallbackModel(config, model);
|
|
168
223
|
const poolScoped = !!route && isPoolCodexRoute(route);
|
|
169
224
|
const health = modelHealth.get(
|
|
170
|
-
healthKey(
|
|
225
|
+
healthKey(
|
|
226
|
+
model,
|
|
227
|
+
resolveRouteFallbackAccountId(route, config, accountId),
|
|
228
|
+
poolScoped,
|
|
229
|
+
),
|
|
171
230
|
);
|
|
172
231
|
return !!health && health.unavailableUntil > now;
|
|
173
232
|
}
|
|
@@ -177,6 +236,7 @@ export function isSubagentModelUnavailable(
|
|
|
177
236
|
config: OcxConfig,
|
|
178
237
|
accountId?: string | null,
|
|
179
238
|
now = Date.now(),
|
|
239
|
+
accountUsabilityOptions?: CodexAccountUsabilityOptions,
|
|
180
240
|
): boolean {
|
|
181
241
|
if (isDisabledFallbackModel(model, config)) return true;
|
|
182
242
|
if (!isRoutableFallbackModel(model, config)) return true;
|
|
@@ -187,11 +247,17 @@ export function isSubagentModelUnavailable(
|
|
|
187
247
|
|
|
188
248
|
// Pool candidates need a usable account. Derive requirement from the resolved
|
|
189
249
|
// route (canonical openai defaults to pool even when codexAccountMode is omitted).
|
|
190
|
-
const resolvedAccountId =
|
|
250
|
+
const resolvedAccountId = resolveRouteFallbackAccountId(route, config, accountId);
|
|
191
251
|
if (!resolvedAccountId) return true;
|
|
192
252
|
if (isCodexAccountPaused(config, resolvedAccountId)) return true;
|
|
193
|
-
if (!isCodexAccountUsable(config, resolvedAccountId)) return true;
|
|
194
|
-
if (
|
|
253
|
+
if (!isCodexAccountUsable(config, resolvedAccountId, accountUsabilityOptions)) return true;
|
|
254
|
+
if (route.codexAccountId !== undefined) {
|
|
255
|
+
// An account-qualified route is pinned and cannot consume Pool's recovery-probe
|
|
256
|
+
// escape hatch. Honor both account-wide and model-scoped cooldowns so fallback
|
|
257
|
+
// advances instead of selecting a candidate that exact auth will reject.
|
|
258
|
+
const quotaScope = codexQuotaScopeForModel(route.modelId);
|
|
259
|
+
if (getCodexQuotaHealthSnapshot(resolvedAccountId, quotaScope, now) !== null) return true;
|
|
260
|
+
} else if (
|
|
195
261
|
isCodexAccountInCooldown(resolvedAccountId, now)
|
|
196
262
|
&& !canAcquireCodexQuotaProbeLease(resolvedAccountId, now)
|
|
197
263
|
) {
|
|
@@ -207,8 +273,10 @@ export function selectAvailableSubagentModel(
|
|
|
207
273
|
accountId?: string | null,
|
|
208
274
|
now = Date.now(),
|
|
209
275
|
nativeFallbackOnly = false,
|
|
276
|
+
accountUsabilityOptions?: CodexAccountUsabilityOptions,
|
|
277
|
+
trailingFallback: readonly string[] = [],
|
|
210
278
|
): { model: string; rewritten: boolean; skipped: string[] } {
|
|
211
|
-
const chain = normalizedChain(primary, config, extraFallback);
|
|
279
|
+
const chain = normalizedChain(primary, config, extraFallback, trailingFallback);
|
|
212
280
|
const skipped: string[] = [];
|
|
213
281
|
for (const candidate of chain) {
|
|
214
282
|
if (nativeFallbackOnly) {
|
|
@@ -218,7 +286,7 @@ export function selectAvailableSubagentModel(
|
|
|
218
286
|
continue;
|
|
219
287
|
}
|
|
220
288
|
}
|
|
221
|
-
if (isSubagentModelUnavailable(candidate, config, accountId, now)) {
|
|
289
|
+
if (isSubagentModelUnavailable(candidate, config, accountId, now, accountUsabilityOptions)) {
|
|
222
290
|
skipped.push(candidate);
|
|
223
291
|
continue;
|
|
224
292
|
}
|
|
@@ -240,7 +308,11 @@ export function noteSubagentModelFailure(
|
|
|
240
308
|
const route = tryRouteFallbackModel(config, model);
|
|
241
309
|
const poolScoped = !!route && isPoolCodexRoute(route);
|
|
242
310
|
modelHealth.set(
|
|
243
|
-
healthKey(
|
|
311
|
+
healthKey(
|
|
312
|
+
model,
|
|
313
|
+
resolveRouteFallbackAccountId(route, config, accountId),
|
|
314
|
+
poolScoped,
|
|
315
|
+
),
|
|
244
316
|
{
|
|
245
317
|
unavailableUntil: now + interval,
|
|
246
318
|
reason: "quota_exhausted",
|
|
@@ -324,13 +396,16 @@ export function readCodexAgentModel(role: string, codexHome = CODEX_HOME): strin
|
|
|
324
396
|
export function resolveAgentModelFallbackForPrimary(
|
|
325
397
|
primary: string,
|
|
326
398
|
codexHome = CODEX_HOME,
|
|
399
|
+
namespaces?: unknown,
|
|
327
400
|
): string[] {
|
|
328
401
|
const merged: string[] = [];
|
|
329
402
|
const seen = new Set<string>();
|
|
330
403
|
const push = (model: string | null | undefined) => {
|
|
331
404
|
if (!model || model.trim() === "") return;
|
|
332
405
|
const trimmed = model.trim();
|
|
333
|
-
|
|
406
|
+
// Match global-chain de-dupe: keep account-selector prefixes case-sensitive when
|
|
407
|
+
// those selectors are configured, while ordinary provider/model ids stay case-insensitive.
|
|
408
|
+
const key = fallbackChainKey(trimmed, namespaces);
|
|
334
409
|
if (seen.has(key)) return;
|
|
335
410
|
seen.add(key);
|
|
336
411
|
merged.push(trimmed);
|
|
@@ -343,18 +418,66 @@ export function resolveAgentModelFallbackForPrimary(
|
|
|
343
418
|
return merged;
|
|
344
419
|
}
|
|
345
420
|
|
|
421
|
+
function subagentQuotaPrimeBlockedByHostCircuit(config: OcxConfig): boolean {
|
|
422
|
+
if (normalizeUpstreamHostCircuitThreshold(config.upstreamHostCircuitThreshold) === 0) return false;
|
|
423
|
+
const key = upstreamHostHealthKey(OPENAI_CODEX_PROVIDER_ID, CODEX_FORWARD_ORIGIN);
|
|
424
|
+
return getUpstreamHostHealth(key)?.cooldownUntil !== undefined;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* Per-primary-model fallback chains from opencodex config (#1190).
|
|
429
|
+
*
|
|
430
|
+
* Storing `model_fallback` inside `$CODEX_HOME/agents/*.toml` makes Codex >= 0.146
|
|
431
|
+
* reject the whole role file as an unknown field. The config-keyed map is the
|
|
432
|
+
* supported home for that metadata; keys match the requested primary model id,
|
|
433
|
+
* using the same raw/encoded slug tolerance as the TOML role lookup.
|
|
434
|
+
*/
|
|
435
|
+
export function resolveConfiguredModelFallbackForPrimary(
|
|
436
|
+
primary: string,
|
|
437
|
+
config: OcxConfig,
|
|
438
|
+
): string[] {
|
|
439
|
+
const byModel = config.subagentModelFallbackByModel;
|
|
440
|
+
if (!byModel || typeof byModel !== "object") return [];
|
|
441
|
+
const entries: string[] = [];
|
|
442
|
+
const seen = new Set<string>();
|
|
443
|
+
const push = (model: string) => {
|
|
444
|
+
const trimmed = model.trim();
|
|
445
|
+
if (trimmed === "") return;
|
|
446
|
+
const key = fallbackChainKey(trimmed, config.codexAccountNamespaces);
|
|
447
|
+
if (seen.has(key)) return;
|
|
448
|
+
seen.add(key);
|
|
449
|
+
entries.push(trimmed);
|
|
450
|
+
};
|
|
451
|
+
for (const [key, chain] of Object.entries(byModel)) {
|
|
452
|
+
if (!slugsEquivalent(key, primary)) continue;
|
|
453
|
+
for (const model of chain) push(model);
|
|
454
|
+
}
|
|
455
|
+
return entries;
|
|
456
|
+
}
|
|
457
|
+
|
|
346
458
|
/**
|
|
347
459
|
* Best-effort quota refresh before subagent model selection.
|
|
348
460
|
* Concurrent callers share one in-flight promise. The success TTL is updated only
|
|
349
461
|
* after a successful refresh so failures remain retryable. Errors are swallowed so
|
|
350
|
-
* spawn routing can continue.
|
|
462
|
+
* spawn routing can continue. When the canonical ChatGPT origin is circuit-blocked,
|
|
463
|
+
* cached quota is used instead of sending credential-bearing usage probes to the
|
|
464
|
+
* same origin before the request's final host admission check.
|
|
351
465
|
*/
|
|
352
|
-
export function maybePrimeSubagentQuota(
|
|
466
|
+
export function maybePrimeSubagentQuota(
|
|
467
|
+
config: OcxConfig,
|
|
468
|
+
now = Date.now(),
|
|
469
|
+
options: { nativeMainReadsForbidden?: boolean } = {},
|
|
470
|
+
): Promise<void> {
|
|
471
|
+
if (options.nativeMainReadsForbidden) return Promise.resolve();
|
|
472
|
+
if (subagentQuotaPrimeBlockedByHostCircuit(config)) return Promise.resolve();
|
|
353
473
|
if (quotaPrimeInFlight) return quotaPrimeInFlight;
|
|
354
474
|
if (!shouldPrimeSubagentQuota(config, now)) return Promise.resolve();
|
|
355
475
|
|
|
356
476
|
quotaPrimeInFlight = (async () => {
|
|
357
477
|
try {
|
|
478
|
+
// Re-check after claiming single-flight ownership so a circuit opened by
|
|
479
|
+
// a concurrent request cannot race us into a fresh usage-probe pass.
|
|
480
|
+
if (subagentQuotaPrimeBlockedByHostCircuit(config)) return;
|
|
358
481
|
if (subagentQuotaPrimeForTests) {
|
|
359
482
|
await subagentQuotaPrimeForTests(config, "subagent-spawn");
|
|
360
483
|
} else {
|
|
@@ -391,18 +514,28 @@ export function applySubagentModelFallback(
|
|
|
391
514
|
accountId?: string | null,
|
|
392
515
|
now = Date.now(),
|
|
393
516
|
nativeFallbackOnly = false,
|
|
517
|
+
accountUsabilityOptions?: CodexAccountUsabilityOptions,
|
|
394
518
|
): { from?: string; to?: string; skipped?: string[] } | null {
|
|
395
519
|
if (!isThreadSpawnRequest(headers)) return null;
|
|
396
|
-
const
|
|
520
|
+
const tomlRoleFallback = resolveAgentModelFallbackForPrimary(
|
|
521
|
+
parsed.modelId,
|
|
522
|
+
getCodexHome(),
|
|
523
|
+
config.codexAccountNamespaces,
|
|
524
|
+
);
|
|
525
|
+
// Config-keyed chains are the supported per-role home (#1190); TOML `model_fallback`
|
|
526
|
+
// stays readable for backwards compatibility with homes written before Codex 0.146.
|
|
527
|
+
const configuredFallback = resolveConfiguredModelFallbackForPrimary(parsed.modelId, config);
|
|
397
528
|
const globalFallback = config.subagentModelFallback ?? [];
|
|
398
|
-
if (globalFallback.length === 0 &&
|
|
529
|
+
if (globalFallback.length === 0 && configuredFallback.length === 0 && tomlRoleFallback.length === 0) return null;
|
|
399
530
|
const selection = selectAvailableSubagentModel(
|
|
400
531
|
parsed.modelId,
|
|
401
532
|
config,
|
|
402
|
-
|
|
533
|
+
configuredFallback,
|
|
403
534
|
accountId,
|
|
404
535
|
now,
|
|
405
536
|
nativeFallbackOnly,
|
|
537
|
+
accountUsabilityOptions,
|
|
538
|
+
tomlRoleFallback,
|
|
406
539
|
);
|
|
407
540
|
if (!selection.rewritten) return selection.skipped.length > 0
|
|
408
541
|
? { from: parsed.modelId, to: parsed.modelId, skipped: selection.skipped }
|
|
@@ -419,33 +552,192 @@ export function subagentFallbackGuidanceText(config: OcxConfig): string {
|
|
|
419
552
|
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
553
|
}
|
|
421
554
|
|
|
422
|
-
const
|
|
555
|
+
const TOML_MODEL_FALLBACK_KEY = /^\s*(?:model_fallback|"model_fallback"|'model_fallback')\s*=/;
|
|
556
|
+
|
|
557
|
+
type TomlModelFallbackField = { present: false; value: null } | { present: true; value: string[] | null };
|
|
558
|
+
|
|
559
|
+
type TomlScanState = {
|
|
560
|
+
inMultilineString: '"""' | "'''" | null;
|
|
561
|
+
arrayDepth: number;
|
|
562
|
+
};
|
|
563
|
+
|
|
564
|
+
/** Track TOML strings, comments, and array brackets on one line. */
|
|
565
|
+
function scanTomlLine(line: string, state: TomlScanState): void {
|
|
566
|
+
let i = 0;
|
|
567
|
+
while (i < line.length) {
|
|
568
|
+
const ch = line[i]!;
|
|
569
|
+
if (ch === "#") return;
|
|
570
|
+
if (ch === '"' || ch === "'") {
|
|
571
|
+
const delimiter = ch.repeat(3);
|
|
572
|
+
if (line.startsWith(delimiter, i)) {
|
|
573
|
+
const end = findTomlMultilineStringEnd(line, i + 3, ch);
|
|
574
|
+
if (end === -1) {
|
|
575
|
+
state.inMultilineString = delimiter as '"""' | "'''";
|
|
576
|
+
return;
|
|
577
|
+
}
|
|
578
|
+
i = end + 3;
|
|
579
|
+
continue;
|
|
580
|
+
}
|
|
581
|
+
i++;
|
|
582
|
+
while (i < line.length) {
|
|
583
|
+
if (ch === '"' && line[i] === "\\") {
|
|
584
|
+
i += 2;
|
|
585
|
+
continue;
|
|
586
|
+
}
|
|
587
|
+
if (line[i] === ch) break;
|
|
588
|
+
i++;
|
|
589
|
+
}
|
|
590
|
+
i++;
|
|
591
|
+
continue;
|
|
592
|
+
}
|
|
593
|
+
if (ch === "[") state.arrayDepth++;
|
|
594
|
+
else if (ch === "]") state.arrayDepth = Math.max(0, state.arrayDepth - 1);
|
|
595
|
+
i++;
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
/** Parse a TOML string starting at `start`; returns the decoded value and end offset. */
|
|
600
|
+
function parseTomlStringAt(text: string, start: number): { value: string; end: number } | null {
|
|
601
|
+
const quote = text[start]!;
|
|
602
|
+
const delimiter = quote.repeat(3);
|
|
603
|
+
if (text.startsWith(delimiter, start)) {
|
|
604
|
+
const end = findTomlMultilineStringEnd(text, start + 3, quote);
|
|
605
|
+
if (end === -1) return null;
|
|
606
|
+
let value = text.slice(start + 3, end).trim();
|
|
607
|
+
if (quote === '"') value = value.replace(/\\"/g, '"').replace(/\\\\/g, "\\");
|
|
608
|
+
return { value, end: end + 3 };
|
|
609
|
+
}
|
|
610
|
+
let i = start + 1;
|
|
611
|
+
let value = "";
|
|
612
|
+
while (i < text.length) {
|
|
613
|
+
const ch = text[i]!;
|
|
614
|
+
if (quote === '"' && ch === "\\") {
|
|
615
|
+
const next = text[i + 1];
|
|
616
|
+
if (next === '"' || next === "\\") {
|
|
617
|
+
value += next;
|
|
618
|
+
i += 2;
|
|
619
|
+
continue;
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
if (ch === quote) return { value, end: i + 1 };
|
|
623
|
+
value += ch;
|
|
624
|
+
i++;
|
|
625
|
+
}
|
|
626
|
+
return null;
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
function findTomlMultilineStringEnd(text: string, from: number, quote: string): number {
|
|
630
|
+
const delimiter = quote.repeat(3);
|
|
631
|
+
let index = text.indexOf(delimiter, from);
|
|
632
|
+
while (index !== -1) {
|
|
633
|
+
if (quote === "'") return index;
|
|
634
|
+
let backslashes = 0;
|
|
635
|
+
for (let j = index - 1; j >= 0 && text[j] === "\\"; j--) backslashes += 1;
|
|
636
|
+
if (backslashes % 2 === 0) return index;
|
|
637
|
+
index = text.indexOf(delimiter, index + 1);
|
|
638
|
+
}
|
|
639
|
+
return -1;
|
|
640
|
+
}
|
|
423
641
|
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
642
|
+
/** After an array close, only horizontal whitespace, an inline comment, or the line end is valid. */
|
|
643
|
+
function isValidTomlArrayTail(text: string, from: number): boolean {
|
|
644
|
+
let i = from;
|
|
645
|
+
while (i < text.length && (text[i] === " " || text[i] === "\t")) i += 1;
|
|
646
|
+
if (i >= text.length || text[i] === "\n" || text[i] === "\r") return true;
|
|
647
|
+
if (text[i] === "#") {
|
|
648
|
+
while (i < text.length && text[i] !== "\n") i += 1;
|
|
649
|
+
return true;
|
|
650
|
+
}
|
|
651
|
+
return false;
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
/** Parse a TOML string-array value; null when the value is not a well-formed array of strings. */
|
|
655
|
+
function parseTomlStringArrayValue(text: string): string[] | null {
|
|
656
|
+
const values: string[] = [];
|
|
657
|
+
let i = 0;
|
|
658
|
+
const skipIgnored = () => {
|
|
659
|
+
while (i < text.length) {
|
|
660
|
+
const ch = text[i]!;
|
|
661
|
+
if (ch === "#") {
|
|
662
|
+
while (i < text.length && text[i] !== "\n") i += 1;
|
|
663
|
+
continue;
|
|
664
|
+
}
|
|
665
|
+
if (/\s/.test(ch)) {
|
|
666
|
+
i += 1;
|
|
667
|
+
continue;
|
|
668
|
+
}
|
|
669
|
+
break;
|
|
670
|
+
}
|
|
671
|
+
};
|
|
672
|
+
skipIgnored();
|
|
673
|
+
if (text[i] !== "[") return null;
|
|
674
|
+
i += 1;
|
|
675
|
+
let expectValue = true;
|
|
676
|
+
for (;;) {
|
|
677
|
+
skipIgnored();
|
|
678
|
+
if (i >= text.length) return null;
|
|
679
|
+
const ch = text[i]!;
|
|
680
|
+
if (ch === "]") {
|
|
681
|
+
if (!isValidTomlArrayTail(text, i + 1)) return null;
|
|
682
|
+
return values;
|
|
683
|
+
}
|
|
684
|
+
if (ch === ",") {
|
|
685
|
+
if (expectValue) return null; // leading or doubled comma
|
|
686
|
+
expectValue = true;
|
|
687
|
+
i += 1;
|
|
688
|
+
continue;
|
|
689
|
+
}
|
|
690
|
+
if (ch === '"' || ch === "'") {
|
|
691
|
+
if (!expectValue) return null; // adjacent strings must be comma-separated
|
|
692
|
+
const parsed = parseTomlStringAt(text, i);
|
|
693
|
+
if (!parsed) return null;
|
|
694
|
+
values.push(parsed.value);
|
|
695
|
+
i = parsed.end;
|
|
696
|
+
expectValue = false;
|
|
697
|
+
continue;
|
|
698
|
+
}
|
|
699
|
+
return null;
|
|
700
|
+
}
|
|
427
701
|
}
|
|
428
702
|
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
703
|
+
/**
|
|
704
|
+
* TOML-aware, presence-aware parse of the `model_fallback` field. Quoted keys
|
|
705
|
+
* are recognized, and text inside strings (including multiline strings) is
|
|
706
|
+
* never treated as a key. `present` is true whenever the key exists, even when
|
|
707
|
+
* the value is not a readable string array; `value` is null in that case.
|
|
708
|
+
*/
|
|
709
|
+
function parseTomlModelFallbackField(content: string): TomlModelFallbackField {
|
|
710
|
+
const lines = content.split(/\r?\n/);
|
|
711
|
+
const state: TomlScanState = { inMultilineString: null, arrayDepth: 0 };
|
|
712
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
713
|
+
const line = lines[i]!;
|
|
714
|
+
if (state.inMultilineString) {
|
|
715
|
+
const end = findTomlMultilineStringEnd(line, 0, state.inMultilineString[0]!);
|
|
716
|
+
if (end === -1) continue;
|
|
717
|
+
state.inMultilineString = null;
|
|
718
|
+
scanTomlLine(line.slice(end + 3), state);
|
|
719
|
+
continue;
|
|
720
|
+
}
|
|
721
|
+
if (state.arrayDepth === 0) {
|
|
722
|
+
const key = line.match(TOML_MODEL_FALLBACK_KEY);
|
|
723
|
+
if (key) {
|
|
724
|
+
const rest = `${line.slice(key[0].length)}\n${lines.slice(i + 1).join("\n")}`;
|
|
725
|
+
return { present: true, value: parseTomlStringArrayValue(rest) };
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
scanTomlLine(line, state);
|
|
729
|
+
}
|
|
730
|
+
return { present: false, value: null };
|
|
433
731
|
}
|
|
434
732
|
|
|
435
733
|
export function readAgentModelFallback(filePath: string): string[] | null {
|
|
436
734
|
try {
|
|
437
735
|
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
|
-
}
|
|
736
|
+
const parsed = parseTomlModelFallbackField(content);
|
|
737
|
+
return parsed.present ? parsed.value : null;
|
|
445
738
|
} catch {
|
|
446
739
|
return null;
|
|
447
740
|
}
|
|
448
|
-
return null;
|
|
449
741
|
}
|
|
450
742
|
|
|
451
743
|
export function readCodexAgentModelFallback(role: string, codexHome = CODEX_HOME): string[] {
|
|
@@ -454,6 +746,29 @@ export function readCodexAgentModelFallback(role: string, codexHome = CODEX_HOME
|
|
|
454
746
|
return readAgentModelFallback(file) ?? [];
|
|
455
747
|
}
|
|
456
748
|
|
|
749
|
+
/**
|
|
750
|
+
* True when the role TOML carries a `model_fallback` key, even an empty array.
|
|
751
|
+
* Presence is what matters for the doctor scan: Codex >= 0.146 rejects the
|
|
752
|
+
* field as unknown and skips the whole role regardless of its value. Uses the
|
|
753
|
+
* same TOML-aware parse as the fallback-reading path, so quoted keys count and
|
|
754
|
+
* text inside string literals does not.
|
|
755
|
+
*/
|
|
756
|
+
export function hasCodexAgentModelFallbackField(role: string, codexHome = CODEX_HOME): boolean {
|
|
757
|
+
const file = join(codexHome, "agents", `${role}.toml`);
|
|
758
|
+
if (!existsSync(file)) return false;
|
|
759
|
+
try {
|
|
760
|
+
const content = readFileSync(file, "utf8");
|
|
761
|
+
return parseTomlModelFallbackField(content).present;
|
|
762
|
+
} catch {
|
|
763
|
+
return false;
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
/** Roles whose TOML still carries `model_fallback`, including empty arrays. */
|
|
768
|
+
export function scanCodexAgentRolesWithTomlModelFallback(codexHome = CODEX_HOME): string[] {
|
|
769
|
+
return listCodexAgentRoles(codexHome).filter(role => hasCodexAgentModelFallbackField(role, codexHome));
|
|
770
|
+
}
|
|
771
|
+
|
|
457
772
|
export function listCodexAgentRoles(codexHome = CODEX_HOME): string[] {
|
|
458
773
|
const dir = join(codexHome, "agents");
|
|
459
774
|
if (!existsSync(dir)) return [];
|