@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
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether a `done` event's `stopReason` means the turn was cut short rather than finishing, and
|
|
3
|
+
* which Responses `incomplete_details.reason` it maps to.
|
|
4
|
+
*
|
|
5
|
+
* `stopReason` is an open-ended string and adapters do not agree on a vocabulary: openai-chat and
|
|
6
|
+
* google normalize to `max_tokens`/`content_filter`, Command Code forwards the raw provider or AI
|
|
7
|
+
* SDK value (`length`, `content-filter`, `error`), and Anthropic forwards `stop_reason` verbatim
|
|
8
|
+
* (`refusal`, `model_context_window_exceeded`, ...). A guard that matched only the two canonical
|
|
9
|
+
* strings let those turns read as completed — and, on a compaction turn, install a half-written
|
|
10
|
+
* summary as replacement history (#422).
|
|
11
|
+
*
|
|
12
|
+
* Classifying here keeps that decision independent of which adapter produced the event, and keeps
|
|
13
|
+
* suppression and terminal status in agreement: a turn whose compaction item is withheld must not
|
|
14
|
+
* also report success, or codex-rs receives a completed response with zero compaction items and
|
|
15
|
+
* fatals.
|
|
16
|
+
*
|
|
17
|
+
* Unknown reasons are deliberately NOT truncated. This must never turn a healthy turn into a
|
|
18
|
+
* failure, and an unrecognized value is far more likely an ordinary stop.
|
|
19
|
+
*/
|
|
20
|
+
type TruncationKind = "max_output_tokens" | "content_filter";
|
|
21
|
+
|
|
22
|
+
const TRUNCATED_STOP_REASONS = new Map<string, TruncationKind>([
|
|
23
|
+
// canonical (openai-chat, google)
|
|
24
|
+
["max_tokens", "max_output_tokens"],
|
|
25
|
+
["content_filter", "content_filter"],
|
|
26
|
+
// raw OpenAI / Command Code (AI SDK) finish reasons
|
|
27
|
+
["length", "max_output_tokens"],
|
|
28
|
+
["content-filter", "content_filter"],
|
|
29
|
+
// raw Anthropic stop reasons
|
|
30
|
+
["max_output_tokens", "max_output_tokens"],
|
|
31
|
+
["model_context_window_exceeded", "max_output_tokens"],
|
|
32
|
+
["refusal", "content_filter"],
|
|
33
|
+
// Anthropic documents `pause_turn` as a long-running turn that the client is expected to
|
|
34
|
+
// CONTINUE. Whatever was produced so far is by definition unfinished, so it must not be
|
|
35
|
+
// installed as replacement history.
|
|
36
|
+
["pause_turn", "max_output_tokens"],
|
|
37
|
+
// raw Gemini / Vertex finish reasons
|
|
38
|
+
["malformed_function_call", "content_filter"],
|
|
39
|
+
["malformed_response", "content_filter"],
|
|
40
|
+
["unexpected_tool_call", "content_filter"],
|
|
41
|
+
["safety", "content_filter"],
|
|
42
|
+
["recitation", "content_filter"],
|
|
43
|
+
["blocklist", "content_filter"],
|
|
44
|
+
["prohibited_content", "content_filter"],
|
|
45
|
+
["spii", "content_filter"],
|
|
46
|
+
["image_safety", "content_filter"],
|
|
47
|
+
["language", "content_filter"],
|
|
48
|
+
// Kiro
|
|
49
|
+
["model_context_window_exceeded_exception", "max_output_tokens"],
|
|
50
|
+
]);
|
|
51
|
+
|
|
52
|
+
/** The `incomplete_details.reason` a truncated stop maps to, or undefined for a normal stop. */
|
|
53
|
+
export function truncationReasonFor(stopReason: string | undefined): TruncationKind | undefined {
|
|
54
|
+
if (stopReason === undefined) return undefined;
|
|
55
|
+
return TRUNCATED_STOP_REASONS.get(stopReason.trim().toLowerCase());
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function isTruncatedStopReason(stopReason: string | undefined): boolean {
|
|
59
|
+
return truncationReasonFor(stopReason) !== undefined;
|
|
60
|
+
}
|
package/src/router.ts
CHANGED
|
@@ -1,19 +1,76 @@
|
|
|
1
1
|
import type { CodexAccountMode, OcxConfig, OcxProviderConfig } from "./types";
|
|
2
|
-
import {
|
|
3
|
-
|
|
2
|
+
import {
|
|
3
|
+
getCombo,
|
|
4
|
+
isComboTargetInCooldown,
|
|
5
|
+
preservesPhysicalComboProvider,
|
|
6
|
+
targetKey,
|
|
7
|
+
tryPickComboModel,
|
|
8
|
+
type ComboPick,
|
|
9
|
+
} from "./combos";
|
|
10
|
+
import type { NormalizedComboConfig } from "./combos/types";
|
|
11
|
+
import { hasOwnProvider } from "./config/provider-name";
|
|
12
|
+
import { resolveEnvValue } from "./config";
|
|
4
13
|
import { assertProviderDestinationAllowed } from "./lib/destination-policy";
|
|
5
14
|
import { redactSecretString, redactUrlForLog } from "./lib/redact";
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
15
|
+
import {
|
|
16
|
+
PROVIDER_REGISTRY,
|
|
17
|
+
mergeRegistryStaticHeaders,
|
|
18
|
+
providerCodexAccountMode,
|
|
19
|
+
registryModelServiceTierCapabilityApplies,
|
|
20
|
+
} from "./providers/registry";
|
|
21
|
+
import { applyDirectReasoningEffortContracts, hasLegacyClinePassReasoningEfforts } from "./providers/derive";
|
|
22
|
+
import { cloneFastWire } from "./providers/fastwire";
|
|
23
|
+
import {
|
|
24
|
+
providerMatchesRegistryTransportWithStaticGuards,
|
|
25
|
+
providerSupportsLiveModelDiscovery,
|
|
26
|
+
} from "./providers/static-model-discovery";
|
|
27
|
+
import {
|
|
28
|
+
isCanonicalOpenAiForwardProvider,
|
|
29
|
+
LEGACY_CHATGPT_PROVIDER_ID,
|
|
30
|
+
LEGACY_OPENAI_MULTI_PROVIDER_ID,
|
|
31
|
+
OPENAI_API_PROVIDER_ID,
|
|
32
|
+
OPENAI_CODEX_PROVIDER_ID,
|
|
33
|
+
} from "./providers/openai-tiers";
|
|
34
|
+
import { decodeRoutedModelIdOrThrow, encodeRoutedModelId } from "./providers/slug-codec";
|
|
9
35
|
import { getStaleCached } from "./codex/model-cache";
|
|
36
|
+
import { codexAccountNamespaceEntries } from "./codex/account-namespaces";
|
|
37
|
+
import {
|
|
38
|
+
buildRouteDecisionTrace,
|
|
39
|
+
type RouteDecisionKind,
|
|
40
|
+
type RouteDecisionTraceV1,
|
|
41
|
+
type TraceCandidateInput,
|
|
42
|
+
} from "./routing/trace";
|
|
43
|
+
import { getRoutingProfile, resolvePolicyProfileId } from "./routing/profile";
|
|
44
|
+
import { evaluatePolicyProfile, type PolicyRequestEvidence } from "./routing/evaluator";
|
|
45
|
+
import { assemblePolicyCandidateEvidence } from "./routing/compatibility/assemble";
|
|
46
|
+
|
|
47
|
+
export class NoEligiblePolicyCandidateError extends Error {
|
|
48
|
+
/** Evaluation trace (with per-candidate exclusions) when nothing qualified. */
|
|
49
|
+
readonly trace?: RouteDecisionTraceV1;
|
|
50
|
+
|
|
51
|
+
constructor(readonly profileId: string, trace?: RouteDecisionTraceV1) {
|
|
52
|
+
super(`No eligible candidates for policy profile: ${profileId}`);
|
|
53
|
+
this.name = "NoEligiblePolicyCandidateError";
|
|
54
|
+
this.trace = trace;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
10
57
|
|
|
11
58
|
export interface RouteResult {
|
|
12
59
|
providerName: string;
|
|
13
60
|
provider: OcxProviderConfig;
|
|
14
61
|
modelId: string;
|
|
62
|
+
/** Which deterministic routing path produced this route (RI-01). */
|
|
63
|
+
routeKind: RouteDecisionKind;
|
|
64
|
+
/** Stable wire reason code for the selected route (RI-01). */
|
|
65
|
+
routeReason: string;
|
|
15
66
|
codexAccountMode?: CodexAccountMode;
|
|
67
|
+
/** Exact account selected by an account-qualified native model. */
|
|
68
|
+
codexAccountId?: string;
|
|
69
|
+
/** Public namespace used by the account-qualified selector. */
|
|
70
|
+
codexAccountNamespace?: string;
|
|
16
71
|
combo?: ComboPick;
|
|
72
|
+
/** Bounded route-decision trace (RI-01); never contains secrets. */
|
|
73
|
+
routeDecision?: RouteDecisionTraceV1;
|
|
17
74
|
}
|
|
18
75
|
|
|
19
76
|
const MODEL_PROVIDER_PATTERNS: Array<{ providerNames: string[]; prefixes: string[] }> = [
|
|
@@ -37,10 +94,15 @@ const MODEL_PROVIDER_PATTERNS: Array<{ providerNames: string[]; prefixes: string
|
|
|
37
94
|
* last-known-good live /models cache (may be empty on a cold start; decode then passes
|
|
38
95
|
* unknown ids through unchanged for an honest upstream error).
|
|
39
96
|
*/
|
|
40
|
-
export function knownModelIdsForProvider(
|
|
97
|
+
export function knownModelIdsForProvider(
|
|
98
|
+
provName: string,
|
|
99
|
+
prov: OcxProviderConfig,
|
|
100
|
+
config?: Pick<OcxConfig, "customModels">,
|
|
101
|
+
): string[] {
|
|
41
102
|
const ids = new Set<string>();
|
|
42
103
|
for (const id of prov.models ?? []) ids.add(id);
|
|
43
|
-
|
|
104
|
+
if (prov.defaultModel) ids.add(prov.defaultModel);
|
|
105
|
+
const registry = providerMatchesRegistryTransportWithStaticGuards(provName, prov)
|
|
44
106
|
? PROVIDER_REGISTRY.find(entry => entry.id === provName)
|
|
45
107
|
: undefined;
|
|
46
108
|
for (const id of registry?.models ?? []) ids.add(id);
|
|
@@ -53,10 +115,14 @@ export function knownModelIdsForProvider(provName: string, prov: OcxProviderConf
|
|
|
53
115
|
registry?.modelDefaultReasoningEfforts,
|
|
54
116
|
registry?.modelReasoningEffortMap,
|
|
55
117
|
registry?.modelMaxOutputTokens,
|
|
118
|
+
registry?.modelSupportsServiceTier,
|
|
56
119
|
]) {
|
|
57
120
|
for (const id of Object.keys(map ?? {})) ids.add(id);
|
|
58
121
|
}
|
|
59
122
|
for (const cached of getStaleCached(provName) ?? []) ids.add(cached.id);
|
|
123
|
+
for (const model of config?.customModels ?? []) {
|
|
124
|
+
if (model.provider === provName && model.modelId) ids.add(model.modelId);
|
|
125
|
+
}
|
|
60
126
|
return [...ids];
|
|
61
127
|
}
|
|
62
128
|
|
|
@@ -201,22 +267,28 @@ function usableResolvedApiKey(apiKey: string | undefined): string | undefined {
|
|
|
201
267
|
return typeof resolved === "string" && resolved.trim().length > 0 ? resolved : undefined;
|
|
202
268
|
}
|
|
203
269
|
|
|
204
|
-
function routedProviderConfig(providerName: string, provider: OcxProviderConfig): OcxProviderConfig {
|
|
270
|
+
export function routedProviderConfig(providerName: string, provider: OcxProviderConfig): OcxProviderConfig {
|
|
205
271
|
const registryEntry = PROVIDER_REGISTRY.find(entry => entry.id === providerName);
|
|
206
|
-
if (!registryEntry || !
|
|
272
|
+
if (!registryEntry || !providerMatchesRegistryTransportWithStaticGuards(providerName, provider)) {
|
|
207
273
|
assertProviderDestinationAllowed(providerName, provider);
|
|
208
274
|
return { ...provider, apiKey: usableResolvedApiKey(provider.apiKey) };
|
|
209
275
|
}
|
|
210
276
|
const resolvedApiKey = usableResolvedApiKey(provider.apiKey);
|
|
277
|
+
const staticModelCatalog = !providerSupportsLiveModelDiscovery(providerName, provider);
|
|
278
|
+
const repairLegacyMimoFreeAuth = providerName === "mimo-free"
|
|
279
|
+
&& staticModelCatalog
|
|
280
|
+
&& (provider.authMode === undefined || provider.authMode === "local");
|
|
211
281
|
const explicitKeyOverride = registryEntry.authKind === "oauth"
|
|
212
282
|
&& registryEntry.allowKeyAuthOverride === true
|
|
213
283
|
&& provider.authMode === "key"
|
|
214
284
|
&& resolvedApiKey !== undefined;
|
|
215
285
|
const canonicalAuthMode = explicitKeyOverride
|
|
216
286
|
? "key"
|
|
217
|
-
:
|
|
218
|
-
?
|
|
219
|
-
|
|
287
|
+
: repairLegacyMimoFreeAuth
|
|
288
|
+
? "key"
|
|
289
|
+
: registryEntry.authKind === "forward" || registryEntry.authKind === "oauth"
|
|
290
|
+
? registryEntry.authKind
|
|
291
|
+
: provider.authMode === "forward" ? undefined : provider.authMode;
|
|
220
292
|
const reasoningEffortMap = mergeRecord(registryEntry.reasoningEffortMap, provider.reasoningEffortMap);
|
|
221
293
|
const modelReasoningEffortMap = mergeNestedRecord(registryEntry.modelReasoningEffortMap, provider.modelReasoningEffortMap);
|
|
222
294
|
const modelReasoningEfforts = mergeStringArrayRecord(registryEntry.modelReasoningEfforts, provider.modelReasoningEfforts);
|
|
@@ -225,10 +297,21 @@ function routedProviderConfig(providerName: string, provider: OcxProviderConfig)
|
|
|
225
297
|
? mergePositiveNumberCaps(registryEntry.modelContextWindows, provider.modelContextWindows)
|
|
226
298
|
: mergeRecordFill(registryEntry.modelContextWindows, provider.modelContextWindows);
|
|
227
299
|
const modelInputModalities = mergeRecordFill(registryEntry.modelInputModalities, provider.modelInputModalities);
|
|
300
|
+
// Registry static headers are documented as applying to every upstream request, so they are
|
|
301
|
+
// filled at resolve time rather than only at seed time: a config written before a header
|
|
302
|
+
// existed, or one carrying any header of its own, would otherwise never receive it. User
|
|
303
|
+
// headers win, matched case-insensitively so an override replaces rather than duplicates.
|
|
304
|
+
const headers = mergeRegistryStaticHeaders(registryEntry.staticHeaders, provider.headers);
|
|
228
305
|
const modelMaxInputTokens = providerName === OPENAI_API_PROVIDER_ID
|
|
229
306
|
? mergePositiveNumberCaps(registryEntry.modelMaxInputTokens, provider.modelMaxInputTokens)
|
|
230
307
|
: mergeRecordFill(registryEntry.modelMaxInputTokens, provider.modelMaxInputTokens);
|
|
231
308
|
const modelMaxOutputTokens = mergeRecordFill(registryEntry.modelMaxOutputTokens, provider.modelMaxOutputTokens);
|
|
309
|
+
const modelSupportsServiceTier = mergeRecordFill(
|
|
310
|
+
registryModelServiceTierCapabilityApplies(registryEntry, provider)
|
|
311
|
+
? registryEntry.modelSupportsServiceTier
|
|
312
|
+
: undefined,
|
|
313
|
+
provider.modelSupportsServiceTier,
|
|
314
|
+
);
|
|
232
315
|
const noVisionModels = mergeStringArray(registryEntry.noVisionModels, provider.noVisionModels);
|
|
233
316
|
const noReasoningModels = mergeStringArray(registryEntry.noReasoningModels, provider.noReasoningModels);
|
|
234
317
|
const noTemperatureModels = mergeStringArray(registryEntry.noTemperatureModels, provider.noTemperatureModels);
|
|
@@ -236,6 +319,7 @@ function routedProviderConfig(providerName: string, provider: OcxProviderConfig)
|
|
|
236
319
|
const noPenaltyModels = mergeStringArray(registryEntry.noPenaltyModels, provider.noPenaltyModels);
|
|
237
320
|
const autoToolChoiceOnlyModels = mergeStringArray(registryEntry.autoToolChoiceOnlyModels, provider.autoToolChoiceOnlyModels);
|
|
238
321
|
const preserveReasoningContentModels = mergeStringArray(registryEntry.preserveReasoningContentModels, provider.preserveReasoningContentModels);
|
|
322
|
+
const requiresReasoningPlaceholderModels = mergeStringArray(registryEntry.requiresReasoningPlaceholderModels, provider.requiresReasoningPlaceholderModels);
|
|
239
323
|
const reasoningSplitModels = mergeStringArray(registryEntry.reasoningSplitModels, provider.reasoningSplitModels);
|
|
240
324
|
const thinkingToggleModels = mergeStringArray(registryEntry.thinkingToggleModels, provider.thinkingToggleModels);
|
|
241
325
|
const thinkingBudgetModels = mergeStringArray(registryEntry.thinkingBudgetModels, provider.thinkingBudgetModels);
|
|
@@ -252,15 +336,63 @@ function routedProviderConfig(providerName: string, provider: OcxProviderConfig)
|
|
|
252
336
|
if (userBaseUrlIsResolved) warnIfBaseUrlDiscarded(providerName, userBaseUrl, baseUrl);
|
|
253
337
|
assertProviderDestinationAllowed(providerName, { baseUrl, allowPrivateNetwork: provider.allowPrivateNetwork });
|
|
254
338
|
|
|
255
|
-
|
|
339
|
+
const resolved: OcxProviderConfig = {
|
|
256
340
|
...provider,
|
|
257
341
|
adapter: registryEntry.adapter,
|
|
258
342
|
baseUrl,
|
|
259
343
|
...(provider.responsesPath === undefined && registryEntry.responsesPath !== undefined
|
|
260
344
|
? { responsesPath: registryEntry.responsesPath }
|
|
261
345
|
: {}),
|
|
346
|
+
...(provider.requiresAdjacentResponsesToolResults === undefined
|
|
347
|
+
&& registryEntry.requiresAdjacentResponsesToolResults !== undefined
|
|
348
|
+
? { requiresAdjacentResponsesToolResults: registryEntry.requiresAdjacentResponsesToolResults }
|
|
349
|
+
: {}),
|
|
350
|
+
...(provider.fastWire === undefined && registryEntry.fastWire !== undefined
|
|
351
|
+
? {
|
|
352
|
+
fastWire: cloneFastWire(registryEntry.fastWire),
|
|
353
|
+
}
|
|
354
|
+
: {}),
|
|
355
|
+
...(provider.supportsServiceTier === undefined && registryEntry.supportsServiceTier !== undefined
|
|
356
|
+
? { supportsServiceTier: registryEntry.supportsServiceTier }
|
|
357
|
+
: {}),
|
|
358
|
+
// Registry-only web-search capability: without this backfill a saved provider row reaches
|
|
359
|
+
// the Responses adapter with the flag `undefined`, so the capability gate added in #2262
|
|
360
|
+
// reads "unclassified" and forwards Codex's OpenAI-only `web_search` config fields. xAI
|
|
361
|
+
// rejects the whole request before inference ("Argument not supported:
|
|
362
|
+
// external_web_access"), which killed every routed Grok turn on the Responses lane.
|
|
363
|
+
// enrichProviderFromRegistry() already fills this, but the request path resolves through
|
|
364
|
+
// routedProviderConfig() and never called it.
|
|
365
|
+
...(provider.supportsOpenAiWebSearchToolFields === undefined
|
|
366
|
+
&& registryEntry.supportsOpenAiWebSearchToolFields !== undefined
|
|
367
|
+
? { supportsOpenAiWebSearchToolFields: registryEntry.supportsOpenAiWebSearchToolFields }
|
|
368
|
+
: {}),
|
|
369
|
+
...(provider.supportsResponsesCustomTools === undefined && registryEntry.supportsResponsesCustomTools !== undefined
|
|
370
|
+
? { supportsResponsesCustomTools: registryEntry.supportsResponsesCustomTools }
|
|
371
|
+
: {}),
|
|
372
|
+
...(provider.preserveResponsesReasoningContent === undefined && registryEntry.preserveResponsesReasoningContent !== undefined
|
|
373
|
+
? { preserveResponsesReasoningContent: registryEntry.preserveResponsesReasoningContent }
|
|
374
|
+
: {}),
|
|
375
|
+
// Registry-only client-facing repair policy (#938): fill only when the
|
|
376
|
+
// saved provider has no explicit policy; clone so runtime never aliases
|
|
377
|
+
// the registry constant.
|
|
378
|
+
...(provider.responsesItemIdRepair === undefined && registryEntry.responsesItemIdRepair
|
|
379
|
+
? {
|
|
380
|
+
responsesItemIdRepair: {
|
|
381
|
+
...(registryEntry.responsesItemIdRepair.message ? { message: [...registryEntry.responsesItemIdRepair.message] } : {}),
|
|
382
|
+
...(registryEntry.responsesItemIdRepair.reasoning ? { reasoning: [...registryEntry.responsesItemIdRepair.reasoning] } : {}),
|
|
383
|
+
...(registryEntry.responsesItemIdRepair.repairMissingTerminalIds !== undefined
|
|
384
|
+
? { repairMissingTerminalIds: registryEntry.responsesItemIdRepair.repairMissingTerminalIds }
|
|
385
|
+
: {}),
|
|
386
|
+
...(registryEntry.responsesItemIdRepair.repairInvalidIds !== undefined
|
|
387
|
+
? { repairInvalidIds: registryEntry.responsesItemIdRepair.repairInvalidIds }
|
|
388
|
+
: {}),
|
|
389
|
+
},
|
|
390
|
+
}
|
|
391
|
+
: {}),
|
|
262
392
|
authMode: canonicalAuthMode,
|
|
263
393
|
apiKey: resolvedApiKey,
|
|
394
|
+
...(staticModelCatalog ? { liveModels: false } : {}),
|
|
395
|
+
...(headers ? { headers } : {}),
|
|
264
396
|
// Backfill the Google wire mode + Vertex project/location from the registry when the user
|
|
265
397
|
// config omits them, so a minimal `google-vertex`/`google-antigravity` entry still routes
|
|
266
398
|
// through the correct branch (CCA/Vertex) instead of falling back to AI Studio.
|
|
@@ -268,7 +400,10 @@ function routedProviderConfig(providerName: string, provider: OcxProviderConfig)
|
|
|
268
400
|
...(provider.project === undefined && registryEntry.project !== undefined ? { project: registryEntry.project } : {}),
|
|
269
401
|
...(provider.location === undefined && registryEntry.location !== undefined ? { location: registryEntry.location } : {}),
|
|
270
402
|
...(provider.contextWindow === undefined && registryEntry.contextWindow !== undefined ? { contextWindow: registryEntry.contextWindow } : {}),
|
|
271
|
-
...(provider.reasoningEfforts === undefined
|
|
403
|
+
...((provider.reasoningEfforts === undefined || hasLegacyClinePassReasoningEfforts(providerName, provider))
|
|
404
|
+
&& registryEntry.reasoningEfforts !== undefined
|
|
405
|
+
? { reasoningEfforts: [...registryEntry.reasoningEfforts] }
|
|
406
|
+
: {}),
|
|
272
407
|
...(provider.escapeBuiltinToolNames === undefined && registryEntry.escapeBuiltinToolNames !== undefined ? { escapeBuiltinToolNames: registryEntry.escapeBuiltinToolNames } : {}),
|
|
273
408
|
...(provider.keyOptional === undefined && registryEntry.keyOptional !== undefined ? { keyOptional: registryEntry.keyOptional } : {}),
|
|
274
409
|
...(provider.modelSuffixBracketStrip === undefined && registryEntry.modelSuffixBracketStrip !== undefined ? { modelSuffixBracketStrip: registryEntry.modelSuffixBracketStrip } : {}),
|
|
@@ -276,6 +411,13 @@ function routedProviderConfig(providerName: string, provider: OcxProviderConfig)
|
|
|
276
411
|
// opt-in, while an explicit user `false` keeps overriding registry `true`.
|
|
277
412
|
...(provider.parallelToolCalls === undefined && registryEntry.parallelToolCalls !== undefined ? { parallelToolCalls: registryEntry.parallelToolCalls } : {}),
|
|
278
413
|
...(provider.promptCacheKey === undefined && registryEntry.promptCacheKey !== undefined ? { promptCacheKey: registryEntry.promptCacheKey } : {}),
|
|
414
|
+
...(provider.chatServiceTier === undefined && registryEntry.chatServiceTier !== undefined ? { chatServiceTier: registryEntry.chatServiceTier } : {}),
|
|
415
|
+
...(provider.openaiChatEofTolerance === undefined && registryEntry.openaiChatEofTolerance !== undefined
|
|
416
|
+
? { openaiChatEofTolerance: registryEntry.openaiChatEofTolerance }
|
|
417
|
+
: {}),
|
|
418
|
+
...(provider.reasoningWireFormat === undefined && registryEntry.reasoningWireFormat !== undefined
|
|
419
|
+
? { reasoningWireFormat: registryEntry.reasoningWireFormat }
|
|
420
|
+
: {}),
|
|
279
421
|
...(provider.defaultMaxOutputTokens === undefined && registryEntry.defaultMaxOutputTokens !== undefined
|
|
280
422
|
? { defaultMaxOutputTokens: registryEntry.defaultMaxOutputTokens }
|
|
281
423
|
: {}),
|
|
@@ -283,6 +425,7 @@ function routedProviderConfig(providerName: string, provider: OcxProviderConfig)
|
|
|
283
425
|
...(modelInputModalities ? { modelInputModalities } : {}),
|
|
284
426
|
...(modelMaxInputTokens ? { modelMaxInputTokens } : {}),
|
|
285
427
|
...(modelMaxOutputTokens ? { modelMaxOutputTokens } : {}),
|
|
428
|
+
...(modelSupportsServiceTier ? { modelSupportsServiceTier } : {}),
|
|
286
429
|
...(modelReasoningEfforts ? { modelReasoningEfforts } : {}),
|
|
287
430
|
...(modelDefaultReasoningEfforts ? { modelDefaultReasoningEfforts } : {}),
|
|
288
431
|
...(reasoningEffortMap ? { reasoningEffortMap } : {}),
|
|
@@ -294,10 +437,13 @@ function routedProviderConfig(providerName: string, provider: OcxProviderConfig)
|
|
|
294
437
|
...(noPenaltyModels ? { noPenaltyModels } : {}),
|
|
295
438
|
...(autoToolChoiceOnlyModels ? { autoToolChoiceOnlyModels } : {}),
|
|
296
439
|
...(preserveReasoningContentModels ? { preserveReasoningContentModels } : {}),
|
|
440
|
+
...(requiresReasoningPlaceholderModels ? { requiresReasoningPlaceholderModels } : {}),
|
|
297
441
|
...(reasoningSplitModels ? { reasoningSplitModels } : {}),
|
|
298
442
|
...(thinkingToggleModels ? { thinkingToggleModels } : {}),
|
|
299
443
|
...(thinkingBudgetModels ? { thinkingBudgetModels } : {}),
|
|
300
444
|
};
|
|
445
|
+
applyDirectReasoningEffortContracts(registryEntry, resolved, provider);
|
|
446
|
+
return resolved;
|
|
301
447
|
}
|
|
302
448
|
|
|
303
449
|
function activeProviderEntries(config: OcxConfig): [string, OcxProviderConfig][] {
|
|
@@ -315,6 +461,34 @@ export class NoEnabledOpenAiProviderError extends Error {
|
|
|
315
461
|
}
|
|
316
462
|
}
|
|
317
463
|
|
|
464
|
+
/**
|
|
465
|
+
* One immutable selection trace for a combo request: built once from the
|
|
466
|
+
* initial pick, before any child dispatch. Fallback execution stays in the
|
|
467
|
+
* usage entry's `attempts[]`; the trace never changes after selection.
|
|
468
|
+
*/
|
|
469
|
+
export function comboRouteDecisionTrace(
|
|
470
|
+
config: OcxConfig,
|
|
471
|
+
comboId: string,
|
|
472
|
+
pick: ComboPick,
|
|
473
|
+
requestedModel: string,
|
|
474
|
+
): RouteDecisionTraceV1 {
|
|
475
|
+
const combo = getCombo(config, comboId);
|
|
476
|
+
return buildRouteDecisionTrace({
|
|
477
|
+
requestedModel,
|
|
478
|
+
routeKind: "combo",
|
|
479
|
+
selected: {
|
|
480
|
+
provider: pick.target.provider,
|
|
481
|
+
model: pick.target.model,
|
|
482
|
+
reason: "combo-pick",
|
|
483
|
+
candidateIndex: pick.targetIndex,
|
|
484
|
+
...(combo
|
|
485
|
+
? { tieBreak: combo.strategy === "round-robin" ? "round-robin" : "failover" }
|
|
486
|
+
: {}),
|
|
487
|
+
},
|
|
488
|
+
candidates: combo ? comboRouteCandidates(config, pick, combo) : undefined,
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
|
|
318
492
|
// Codex uses a small number of control-plane model ids that are not part of the public GPT/o
|
|
319
493
|
// naming families. Keep this exact: a broad `codex-*` rule could capture a third-party model.
|
|
320
494
|
const CODEX_INTERNAL_OPENAI_MODELS = new Set(["codex-auto-review"]);
|
|
@@ -324,25 +498,140 @@ function isBareOpenAiFamilyModel(modelId: string): boolean {
|
|
|
324
498
|
&& (/^(?:gpt-|o1-|o3-|o4-)/.test(modelId) || CODEX_INTERNAL_OPENAI_MODELS.has(modelId));
|
|
325
499
|
}
|
|
326
500
|
|
|
327
|
-
function routeResult(
|
|
501
|
+
function routeResult(
|
|
502
|
+
providerName: string,
|
|
503
|
+
provider: OcxProviderConfig,
|
|
504
|
+
modelId: string,
|
|
505
|
+
routeKind: RouteDecisionKind,
|
|
506
|
+
routeReason: string,
|
|
507
|
+
): RouteResult {
|
|
328
508
|
const codexAccountMode = providerCodexAccountMode(providerName, provider);
|
|
329
509
|
return {
|
|
330
510
|
providerName,
|
|
331
511
|
provider: routedProviderConfig(providerName, provider),
|
|
332
512
|
modelId,
|
|
513
|
+
routeKind,
|
|
514
|
+
routeReason,
|
|
333
515
|
...(codexAccountMode ? { codexAccountMode } : {}),
|
|
334
516
|
};
|
|
335
517
|
}
|
|
336
518
|
|
|
337
|
-
|
|
519
|
+
/**
|
|
520
|
+
* Candidate evidence for a combo route: every configured target with its
|
|
521
|
+
* selection-time eligibility and exclusion reasons. Purely observational; the
|
|
522
|
+
* pick already happened and this never re-selects.
|
|
523
|
+
*/
|
|
524
|
+
function comboRouteCandidates(
|
|
525
|
+
config: OcxConfig,
|
|
526
|
+
pick: NonNullable<RouteResult["combo"]>,
|
|
527
|
+
combo: NormalizedComboConfig,
|
|
528
|
+
): TraceCandidateInput[] {
|
|
529
|
+
const now = Date.now();
|
|
530
|
+
return combo.targets.map((target, index) => {
|
|
531
|
+
const key = targetKey(target);
|
|
532
|
+
const provider = config.providers[target.provider];
|
|
533
|
+
const configured = provider !== undefined;
|
|
534
|
+
const enabled = configured && provider.disabled !== true;
|
|
535
|
+
const inCooldown = isComboTargetInCooldown(pick.comboId, target, now);
|
|
536
|
+
const isSelected = index === pick.targetIndex;
|
|
537
|
+
// The pick's `attempted` list includes the winner itself; only non-selected
|
|
538
|
+
// targets can be "already-attempted" (fallback picks exclude earlier tries).
|
|
539
|
+
const alreadyAttempted = !isSelected && pick.attempted.includes(key);
|
|
540
|
+
const exclusions: TraceCandidateInput["exclusions"] = [];
|
|
541
|
+
if (!configured) exclusions.push({ code: "unconfigured" });
|
|
542
|
+
if (configured && !enabled) exclusions.push({ code: "disabled" });
|
|
543
|
+
if (inCooldown) exclusions.push({ code: "cooldown" });
|
|
544
|
+
if (isSelected && inCooldown) exclusions.push({ code: "selected-despite-cooldown" });
|
|
545
|
+
if (!isSelected && alreadyAttempted && exclusions.length === 0) {
|
|
546
|
+
exclusions.push({ code: "already-attempted" });
|
|
547
|
+
}
|
|
548
|
+
if (!isSelected && exclusions.length === 0) exclusions.push({ code: "not-selected" });
|
|
549
|
+
return {
|
|
550
|
+
provider: target.provider,
|
|
551
|
+
model: target.model,
|
|
552
|
+
eligible: enabled && !inCooldown && !alreadyAttempted,
|
|
553
|
+
exclusions,
|
|
554
|
+
};
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
function routeModelInternal(
|
|
559
|
+
config: OcxConfig,
|
|
560
|
+
modelId: string,
|
|
561
|
+
bypassCombos: boolean,
|
|
562
|
+
policyEvidence?: PolicyRequestEvidence,
|
|
563
|
+
): RouteResult {
|
|
564
|
+
const slash = modelId.indexOf("/");
|
|
565
|
+
// Policy namespace is system-reserved: an explicit `policy/<id>` or a
|
|
566
|
+
// configured profile alias executes the policy evaluator and routes the
|
|
567
|
+
// selected candidate. Only explicit requests reach this branch; concrete
|
|
568
|
+
// recursive targets skip policy resolution entirely (bypassCombos) so an
|
|
569
|
+
// alias matching a selected candidate can never recurse, and a
|
|
570
|
+
// `policy/<id>` without a configured profile falls through to normal
|
|
571
|
+
// provider/default resolution instead of failing.
|
|
572
|
+
const policyId = !bypassCombos ? resolvePolicyProfileId(config, modelId) : null;
|
|
573
|
+
const profile = policyId ? getRoutingProfile(config, policyId) : undefined;
|
|
574
|
+
if (profile && policyId) {
|
|
575
|
+
// One clock read per decision keeps candidate evidence, exclusions, and
|
|
576
|
+
// scores mutually consistent and reproducible.
|
|
577
|
+
const now = Date.now();
|
|
578
|
+
const candidateEvidence = assemblePolicyCandidateEvidence(config, profile, now, {
|
|
579
|
+
routedProviderConfig,
|
|
580
|
+
});
|
|
581
|
+
const evaluation = evaluatePolicyProfile(config, policyId, policyEvidence ?? {}, candidateEvidence, now);
|
|
582
|
+
if (evaluation.selectedIndex === null) {
|
|
583
|
+
throw new NoEligiblePolicyCandidateError(policyId, evaluation.trace);
|
|
584
|
+
}
|
|
585
|
+
const selected = evaluation.candidates[evaluation.selectedIndex]!;
|
|
586
|
+
const concrete = `${selected.provider}/${selected.model}`;
|
|
587
|
+
const routed = routeModelInternal(config, concrete, true);
|
|
588
|
+
return {
|
|
589
|
+
...routed,
|
|
590
|
+
routeKind: "policy" as const,
|
|
591
|
+
routeReason: "policy-selected",
|
|
592
|
+
routeDecision: evaluation.trace,
|
|
593
|
+
};
|
|
594
|
+
}
|
|
595
|
+
if (slash > 0) {
|
|
596
|
+
const namespace = modelId.slice(0, slash);
|
|
597
|
+
const binding = codexAccountNamespaceEntries(config)
|
|
598
|
+
.find(([candidate]) => candidate === namespace);
|
|
599
|
+
if (binding) {
|
|
600
|
+
const nativeModelId = modelId.slice(slash + 1);
|
|
601
|
+
if (!isBareOpenAiFamilyModel(nativeModelId)) {
|
|
602
|
+
throw new Error(`Codex account namespace ${namespace} only supports native OpenAI model ids`);
|
|
603
|
+
}
|
|
604
|
+
const provider = config.providers[OPENAI_CODEX_PROVIDER_ID];
|
|
605
|
+
if (!provider || provider.disabled === true) {
|
|
606
|
+
throw new NoEnabledOpenAiProviderError(nativeModelId);
|
|
607
|
+
}
|
|
608
|
+
// Registry routing backfills an omitted authMode on the built-in OpenAI row to forward.
|
|
609
|
+
// Mirror only that default here; explicit non-forward modes still fail closed.
|
|
610
|
+
const providerForCanonicalCheck = provider.authMode === undefined
|
|
611
|
+
? { ...provider, authMode: "forward" as const }
|
|
612
|
+
: provider;
|
|
613
|
+
if (!isCanonicalOpenAiForwardProvider(providerForCanonicalCheck)) {
|
|
614
|
+
throw new NoEnabledOpenAiProviderError(nativeModelId);
|
|
615
|
+
}
|
|
616
|
+
return {
|
|
617
|
+
...routeResult(OPENAI_CODEX_PROVIDER_ID, provider, nativeModelId, "explicit-account", "account-namespace"),
|
|
618
|
+
// Exact account injection uses the pool credential machinery even when the canonical
|
|
619
|
+
// provider is globally Direct. The fixed id bypasses pool selection entirely.
|
|
620
|
+
codexAccountMode: "pool",
|
|
621
|
+
codexAccountId: binding[1],
|
|
622
|
+
codexAccountNamespace: namespace,
|
|
623
|
+
};
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
|
|
338
627
|
if (!bypassCombos && !preservesPhysicalComboProvider(config)) {
|
|
339
628
|
const combo = tryPickComboModel(config, modelId);
|
|
340
629
|
if (combo) {
|
|
341
630
|
const concrete = `${combo.target.provider}/${combo.target.model}`;
|
|
342
631
|
// The selected target is already a concrete provider/model reference. Resolve it without
|
|
343
632
|
// consulting combo aliases again, otherwise an alias that shadows the target can recurse.
|
|
344
|
-
const routed = routeModelInternal(config, concrete, true);
|
|
345
|
-
return { ...routed, combo };
|
|
633
|
+
const routed = routeModelInternal(config, concrete, true, undefined);
|
|
634
|
+
return { ...routed, combo, routeKind: "combo" as const, routeReason: "combo-pick" };
|
|
346
635
|
}
|
|
347
636
|
}
|
|
348
637
|
|
|
@@ -350,7 +639,6 @@ function routeModelInternal(config: OcxConfig, modelId: string, bypassCombos: bo
|
|
|
350
639
|
// Only triggers when the prefix matches a CONFIGURED provider, so genuine
|
|
351
640
|
// slash-containing model ids (e.g. "anthropic/claude-...") fall through when
|
|
352
641
|
// no such provider exists.
|
|
353
|
-
const slash = modelId.indexOf("/");
|
|
354
642
|
if (slash > 0) {
|
|
355
643
|
const provName = modelId.slice(0, slash);
|
|
356
644
|
if (provName === LEGACY_CHATGPT_PROVIDER_ID || provName === LEGACY_OPENAI_MULTI_PROVIDER_ID) {
|
|
@@ -359,27 +647,37 @@ function routeModelInternal(config: OcxConfig, modelId: string, bypassCombos: bo
|
|
|
359
647
|
if (hasOwnProvider(config.providers, provName)) {
|
|
360
648
|
const prov = config.providers[provName];
|
|
361
649
|
if (prov.disabled === true) throw new Error(`Provider is disabled: ${provName}`);
|
|
362
|
-
const known = knownModelIdsForProvider(provName, prov);
|
|
650
|
+
const known = knownModelIdsForProvider(provName, prov, config);
|
|
363
651
|
// Self-namespaced native id — the vendor segment equals the provider id, so the FULL ref is
|
|
364
652
|
// itself a known model (e.g. orcarouter/auto). Route it whole instead of stripping to the
|
|
365
653
|
// remainder, which would send a bare `auto` the upstream cannot resolve.
|
|
366
|
-
if (known.includes(modelId))
|
|
654
|
+
if (known.includes(modelId)) {
|
|
655
|
+
return routeResult(provName, prov, modelId, "explicit-provider", "explicit-provider-namespace");
|
|
656
|
+
}
|
|
367
657
|
// Codex-facing alias ids (`provider/vendor-model`) decode back to the native
|
|
368
658
|
// slash id via an exact known-id lookup; raw full-slash selectors keep working.
|
|
369
|
-
return routeResult(
|
|
659
|
+
return routeResult(
|
|
660
|
+
provName,
|
|
661
|
+
prov,
|
|
662
|
+
decodeRoutedModelIdOrThrow(modelId.slice(slash + 1), known),
|
|
663
|
+
"explicit-provider",
|
|
664
|
+
"explicit-provider-namespace",
|
|
665
|
+
);
|
|
370
666
|
}
|
|
371
667
|
}
|
|
372
668
|
|
|
373
669
|
if (isBareOpenAiFamilyModel(modelId)) {
|
|
374
670
|
const provider = config.providers[OPENAI_CODEX_PROVIDER_ID];
|
|
375
|
-
if (provider && provider.disabled !== true)
|
|
671
|
+
if (provider && provider.disabled !== true) {
|
|
672
|
+
return routeResult(OPENAI_CODEX_PROVIDER_ID, provider, modelId, "native", "native-family");
|
|
673
|
+
}
|
|
376
674
|
throw new NoEnabledOpenAiProviderError(modelId);
|
|
377
675
|
}
|
|
378
676
|
|
|
379
677
|
for (const [provName, prov] of activeProviderEntries(config)) {
|
|
380
678
|
if (prov.defaultModel === modelId
|
|
381
679
|
|| (typeof prov.defaultModel === "string" && encodeRoutedModelId(prov.defaultModel) === modelId)) {
|
|
382
|
-
return routeResult(provName, prov, prov.defaultModel as string);
|
|
680
|
+
return routeResult(provName, prov, prov.defaultModel as string, "explicit-provider", "configured-default-model");
|
|
383
681
|
}
|
|
384
682
|
}
|
|
385
683
|
|
|
@@ -389,7 +687,9 @@ function routeModelInternal(config: OcxConfig, modelId: string, bypassCombos: bo
|
|
|
389
687
|
for (const [provName, prov] of activeProviderEntries(config)) {
|
|
390
688
|
if (prov.models && Array.isArray(prov.models)) {
|
|
391
689
|
const hit = (prov.models as string[]).find(id => id === modelId || encodeRoutedModelId(id) === modelId);
|
|
392
|
-
if (hit !== undefined)
|
|
690
|
+
if (hit !== undefined) {
|
|
691
|
+
return routeResult(provName, prov, hit, "explicit-provider", "configured-model-list");
|
|
692
|
+
}
|
|
393
693
|
}
|
|
394
694
|
}
|
|
395
695
|
|
|
@@ -399,26 +699,62 @@ function routeModelInternal(config: OcxConfig, modelId: string, bypassCombos: bo
|
|
|
399
699
|
if (hasOwnProvider(config.providers, config.defaultProvider)) {
|
|
400
700
|
const defaultProv = config.providers[config.defaultProvider];
|
|
401
701
|
if (defaultProv.disabled === true) throw new Error(`Default provider is disabled: ${config.defaultProvider}`);
|
|
402
|
-
return routeResult(config.defaultProvider, defaultProv, modelId);
|
|
702
|
+
return routeResult(config.defaultProvider, defaultProv, modelId, "default-provider", "default-provider");
|
|
403
703
|
}
|
|
404
704
|
|
|
405
705
|
throw new Error(`No provider configured for model: ${modelId}`);
|
|
406
706
|
}
|
|
407
707
|
|
|
408
|
-
export function routeModel(
|
|
409
|
-
|
|
708
|
+
export function routeModel(
|
|
709
|
+
config: OcxConfig,
|
|
710
|
+
modelId: string,
|
|
711
|
+
policyEvidence?: PolicyRequestEvidence,
|
|
712
|
+
): RouteResult {
|
|
713
|
+
const route = routeModelInternal(config, modelId, false, policyEvidence);
|
|
714
|
+
// Policy routes carry a full evaluation trace already; never rebuild it.
|
|
715
|
+
if (route.routeDecision) return route;
|
|
716
|
+
const accountRef = route.codexAccountNamespace;
|
|
717
|
+
const combo = route.combo ? getCombo(config, route.combo.comboId) : undefined;
|
|
718
|
+
route.routeDecision = buildRouteDecisionTrace({
|
|
719
|
+
requestedModel: modelId,
|
|
720
|
+
routeKind: route.routeKind,
|
|
721
|
+
selected: {
|
|
722
|
+
provider: route.providerName,
|
|
723
|
+
model: route.modelId,
|
|
724
|
+
...(accountRef ? { accountRef } : {}),
|
|
725
|
+
reason: route.routeReason,
|
|
726
|
+
...(route.combo ? { candidateIndex: route.combo.targetIndex } : {}),
|
|
727
|
+
...(combo
|
|
728
|
+
? { tieBreak: combo.strategy === "round-robin" ? "round-robin" : "failover" }
|
|
729
|
+
: {}),
|
|
730
|
+
},
|
|
731
|
+
candidates: route.routeKind === "combo" && route.combo && combo
|
|
732
|
+
? comboRouteCandidates(config, route.combo, combo)
|
|
733
|
+
: undefined,
|
|
734
|
+
});
|
|
735
|
+
return route;
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
/** Resolve a combo-selected provider/model target without consulting public combo aliases again. */
|
|
739
|
+
export function routeConcreteModel(config: OcxConfig, modelId: string): RouteResult {
|
|
740
|
+
return routeModelInternal(config, modelId, true, undefined);
|
|
410
741
|
}
|
|
411
742
|
|
|
412
743
|
function routeByKnownModelPattern(config: OcxConfig, modelId: string): RouteResult | undefined {
|
|
413
744
|
for (const { providerNames, prefixes } of MODEL_PROVIDER_PATTERNS) {
|
|
414
745
|
if (prefixes.some(prefix => modelId.startsWith(prefix))) {
|
|
415
746
|
const matchingProvider = Object.entries(config.providers).find(
|
|
416
|
-
([name]) => providerNames.some(providerName => name === providerName || name.startsWith(`${providerName}-`))
|
|
747
|
+
([name, prov]) => prov.disabled !== true && providerNames.some(providerName => name === providerName || name.startsWith(`${providerName}-`))
|
|
417
748
|
);
|
|
418
749
|
if (matchingProvider) {
|
|
419
750
|
const [provName, prov] = matchingProvider;
|
|
420
|
-
return routeResult(provName, prov, modelId);
|
|
751
|
+
return routeResult(provName, prov, modelId, "explicit-provider", "model-pattern");
|
|
421
752
|
}
|
|
753
|
+
// Deliberately no "first provider with an Anthropic adapter" fallback here. Picking by
|
|
754
|
+
// object insertion order, without checking `models`, `selectedModels`, `disabledModels` or
|
|
755
|
+
// discovery state, silently moves a request onto a provider the operator never chose, with
|
|
756
|
+
// its own privacy and billing consequences (#1697). A classifier turn that needs a specific
|
|
757
|
+
// target gets it from operator-declared `claudeCode.classifierModel` / `classifierFallbacks`.
|
|
422
758
|
}
|
|
423
759
|
}
|
|
424
760
|
return undefined;
|