@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
|
@@ -9,7 +9,8 @@ import { parseRequest } from "../../responses/parser";
|
|
|
9
9
|
import { buildCompactV1Output, COMPACT_PROMPT, decodeCompactionSummary, extractCompactUserMessages } from "../../responses/compaction";
|
|
10
10
|
import { FORWARD_HEADERS, sanitizeReasoningInputContent } from "../../adapters/openai-responses";
|
|
11
11
|
import { expandPreviousResponseInput, previousResponseProviderState, rememberResponseState } from "../../responses/state";
|
|
12
|
-
import { routeModel } from "../../router";
|
|
12
|
+
import { NoEligiblePolicyCandidateError, routeModel } from "../../router";
|
|
13
|
+
import { evidenceFromBody } from "../../routing/request-evidence";
|
|
13
14
|
import {
|
|
14
15
|
advanceComboAfterFailure,
|
|
15
16
|
comboDefaultEffort,
|
|
@@ -27,7 +28,7 @@ import {
|
|
|
27
28
|
import { isInjectionDebugEnabled } from "../../lib/debug-settings";
|
|
28
29
|
import { injectionDebugLog } from "../../lib/injection-debug-log";
|
|
29
30
|
import { modelInList, namespacedToolName } from "../../types";
|
|
30
|
-
import type { AdapterEvent, OcxConfig, OcxParsedRequest, OcxProviderConfig, OcxProviderContinuationState, OcxUsage } from "../../types";
|
|
31
|
+
import type { AdapterEvent, CodexAccountMode, OcxConfig, OcxParsedRequest, OcxProviderConfig, OcxProviderContinuationState, OcxUsage } from "../../types";
|
|
31
32
|
import {
|
|
32
33
|
forceRefreshOAuthAccessSnapshot,
|
|
33
34
|
getOAuthCredentialApiBaseUrl,
|
|
@@ -42,16 +43,21 @@ import { createAdapterEventQueue, preflightAdapterEvents } from "../../adapters/
|
|
|
42
43
|
import {
|
|
43
44
|
applyCodexAuthContextToProvider,
|
|
44
45
|
CodexAccountCooldownError,
|
|
46
|
+
codexMainProfileDrainingResponse,
|
|
45
47
|
cooldownErrorResponse,
|
|
46
48
|
CodexAuthContextError,
|
|
47
49
|
CodexDirectAuthenticationError,
|
|
50
|
+
CodexMainProfileDrainingError,
|
|
48
51
|
CodexPoolAuthenticationError,
|
|
49
52
|
CodexThreadAffinityExpiredError,
|
|
50
53
|
headersForCodexAuthContext,
|
|
54
|
+
materializeCodexUpstreamAuth,
|
|
55
|
+
CodexMainSubstitutionUnavailableError,
|
|
51
56
|
isCodexAuthContextUsable,
|
|
52
57
|
resolveCodexAuthContext,
|
|
53
58
|
codexProbeLeaseId,
|
|
54
59
|
codexProbeQuotaScope,
|
|
60
|
+
releaseCodexAuthContextProbeLease,
|
|
55
61
|
type CodexAuthContext,
|
|
56
62
|
} from "../../codex/auth-context";
|
|
57
63
|
import {
|
|
@@ -59,10 +65,27 @@ import {
|
|
|
59
65
|
recordCodexUpstreamOutcome,
|
|
60
66
|
type CodexUpstreamOutcome,
|
|
61
67
|
} from "../../codex/routing";
|
|
62
|
-
import {
|
|
68
|
+
import {
|
|
69
|
+
fetchWithResetRetry,
|
|
70
|
+
fetchWithTransientRetry,
|
|
71
|
+
applyUpstreamRecoveryInit,
|
|
72
|
+
type UpstreamSendRecovery,
|
|
73
|
+
} from "../../lib/upstream-retry";
|
|
74
|
+
import { classifyTransportFailureKind, transportErrorCode } from "../../lib/upstream-reachability";
|
|
75
|
+
import {
|
|
76
|
+
acquireUpstreamHostAdmission,
|
|
77
|
+
disableUpstreamHostCircuitForKey,
|
|
78
|
+
normalizeUpstreamHostCircuitThreshold,
|
|
79
|
+
recordUpstreamHostFailure,
|
|
80
|
+
releaseUpstreamHostAdmission,
|
|
81
|
+
resetUpstreamHostHealth,
|
|
82
|
+
upstreamHostHealthKey,
|
|
83
|
+
type UpstreamHostAdmissionLease,
|
|
84
|
+
} from "../../codex/upstream-host-health";
|
|
63
85
|
import { ForwardAdmissionCredentialError, validateForwardAdmissionCredential } from "../auth-cors";
|
|
86
|
+
import type { DataPlaneAdmission } from "../auth-cors";
|
|
64
87
|
import { listOpenAiForwardSidecarCandidates, resolveFirstUsableOpenAiSidecar, type ResolvedOpenAiForwardSidecar } from "../../providers/openai-sidecar";
|
|
65
|
-
import { isCanonicalOpenAiForwardProvider, supportsNativeResponsesCompactEndpoint } from "../../providers/openai-tiers";
|
|
88
|
+
import { CODEX_FORWARD_BASE_URL, isCanonicalOpenAiForwardProvider, supportsNativeResponsesCompactEndpoint } from "../../providers/openai-tiers";
|
|
66
89
|
import { slugsEquivalent } from "../../providers/slug-codec";
|
|
67
90
|
import { applyOpenAiVirtualModel, resolveOpenAiCompactModel } from "../../providers/openai-virtual-models";
|
|
68
91
|
import { isUsageDebugEnabled } from "../../usage/debug";
|
|
@@ -72,7 +95,8 @@ import { hasKeyPoolFailover, rotateProviderTransportOn429 } from "../../provider
|
|
|
72
95
|
import { shouldAttemptImageTierRetry } from "../image-retry";
|
|
73
96
|
import { resolveProviderTransport } from "../../providers/xai-transport";
|
|
74
97
|
import type { WsData } from "../ws-bridge";
|
|
75
|
-
import { registerTurn, trackStreamLifetime, unregisterTurn } from "../lifecycle";
|
|
98
|
+
import { codexAccountSelectionForTurn, registerTurn, trackStreamLifetime, unregisterTurn } from "../lifecycle";
|
|
99
|
+
import type { AdmissionLease } from "../../lib/admission";
|
|
76
100
|
import { redactSecretString } from "../../lib/redact";
|
|
77
101
|
import { readBoundedResponseBody } from "../../lib/bounded-body";
|
|
78
102
|
import { supportedLadderFor } from "../effort-policy";
|
|
@@ -99,9 +123,17 @@ import {
|
|
|
99
123
|
} from "../relay";
|
|
100
124
|
import { hasResponsesItemIdRepair, relaySseWithResponsesItemIdRepair } from "../responses-item-id-repair";
|
|
101
125
|
import type { EffectiveSubagentRoster, SpawnAgentSurface } from "../../codex/catalog";
|
|
126
|
+
import { codexAuthContextLogLabel } from "../../codex/account-label";
|
|
102
127
|
|
|
103
|
-
import {
|
|
104
|
-
|
|
128
|
+
import {
|
|
129
|
+
codexAccountGatedCanonicalWireModel,
|
|
130
|
+
decodeRequestErrorResponse,
|
|
131
|
+
handleResponses,
|
|
132
|
+
preAuthUpstreamHostCircuitKey,
|
|
133
|
+
upstreamHostCircuitOpenResponse,
|
|
134
|
+
usesCodexForwardPoolAuth,
|
|
135
|
+
} from "./core";
|
|
136
|
+
import { fetchWithHeaderTimeout, providerFetch, safeHostLabel, safeOriginLabel } from "./fetch-helpers";
|
|
105
137
|
|
|
106
138
|
export const COMPACT_RESPONSE_MAX_BYTES = 32 * 1024 * 1024;
|
|
107
139
|
|
|
@@ -117,10 +149,88 @@ export function compactResponseTooLargeError(): Response {
|
|
|
117
149
|
|
|
118
150
|
|
|
119
151
|
|
|
152
|
+
/**
|
|
153
|
+
* Resolve one eligible pool account other than `excludeAccountId`, and build everything
|
|
154
|
+
* the alternate send needs. Returns null when no alternate exists or construction fails,
|
|
155
|
+
* in which case the caller keeps the first account's rejection intact.
|
|
156
|
+
*
|
|
157
|
+
* Mirrors the auth resolution the native compact branch already does for the first
|
|
158
|
+
* account, so the alternate is built the same way rather than through a second,
|
|
159
|
+
* divergent path.
|
|
160
|
+
*/
|
|
161
|
+
async function resolveAlternateCompactContext(args: {
|
|
162
|
+
req: Request;
|
|
163
|
+
config: OcxConfig;
|
|
164
|
+
route: { provider: OcxProviderConfig; codexAccountMode?: CodexAccountMode };
|
|
165
|
+
selectedModelId: string | undefined;
|
|
166
|
+
excludeAccountId: string | null;
|
|
167
|
+
turnAdmissionLease?: AdmissionLease;
|
|
168
|
+
}): Promise<{ authCtx: CodexAuthContext; provider: OcxProviderConfig; headers: Headers } | null> {
|
|
169
|
+
const { req, config, route, selectedModelId, excludeAccountId, turnAdmissionLease } = args;
|
|
170
|
+
if (!route.codexAccountMode || !excludeAccountId) return null;
|
|
171
|
+
try {
|
|
172
|
+
const authCtx = await resolveCodexAuthContext(req.headers, config, route.codexAccountMode, {
|
|
173
|
+
...(selectedModelId ? { modelId: selectedModelId } : {}),
|
|
174
|
+
excludeAccountId,
|
|
175
|
+
beginCodexAccountSelection: codexAccountSelectionForTurn(turnAdmissionLease),
|
|
176
|
+
});
|
|
177
|
+
if (!authCtx.accountId || authCtx.accountId === excludeAccountId) return null;
|
|
178
|
+
const provider = applyCodexAuthContextToProvider(route.provider, authCtx, route.codexAccountMode);
|
|
179
|
+
const headers = new Headers({ "content-type": "application/json" });
|
|
180
|
+
const selected = headersForCodexAuthContext(req.headers, authCtx);
|
|
181
|
+
for (const name of FORWARD_HEADERS) {
|
|
182
|
+
const value = selected.get(name);
|
|
183
|
+
if (value) headers.set(name, value);
|
|
184
|
+
}
|
|
185
|
+
const override = (provider as { _codexAccountOverride?: { accessToken: string; chatgptAccountId: string } })._codexAccountOverride;
|
|
186
|
+
if (override) {
|
|
187
|
+
headers.set("authorization", `Bearer ${override.accessToken}`);
|
|
188
|
+
headers.set("chatgpt-account-id", override.chatgptAccountId);
|
|
189
|
+
}
|
|
190
|
+
if (provider.apiKey) headers.set("authorization", `Bearer ${resolveEnvValue(provider.apiKey)}`);
|
|
191
|
+
return { authCtx, provider, headers };
|
|
192
|
+
} catch (err) {
|
|
193
|
+
if (err instanceof CodexMainProfileDrainingError) {
|
|
194
|
+
// The native-main fence can start after account A has already rejected the
|
|
195
|
+
// request. Treat the now-fenced main profile as no alternate and preserve A's
|
|
196
|
+
// real rejection instead of replacing it with a synthetic drain response.
|
|
197
|
+
return null;
|
|
198
|
+
}
|
|
199
|
+
// No eligible alternate (all cooled, affinity expired, reauth needed) — the caller
|
|
200
|
+
// returns the first account's rejection unchanged, which is today's behavior.
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Headers a client needs to back off correctly after a pool rejection. The buffered
|
|
207
|
+
* response is rebuilt from scratch, so anything not listed here is dropped — which is
|
|
208
|
+
* what silently discarded `Retry-After` and the reset hints from a 429 before.
|
|
209
|
+
* Deliberately narrow: no hop-by-hop headers, no content-length (the buffered body sets
|
|
210
|
+
* its own), no cookies.
|
|
211
|
+
*/
|
|
212
|
+
const COMPACT_PASSTHROUGH_HEADERS = [
|
|
213
|
+
"retry-after",
|
|
214
|
+
"x-codex-primary-reset-at",
|
|
215
|
+
"x-codex-secondary-reset-at",
|
|
216
|
+
"x-codex-tertiary-reset-at",
|
|
217
|
+
// A relayed 3xx keeps its Location so the client can follow it (#914).
|
|
218
|
+
"location",
|
|
219
|
+
];
|
|
220
|
+
|
|
221
|
+
function compactResponseHeaders(upstream: Response): Headers {
|
|
222
|
+
const headers = new Headers({ "Content-Type": upstream.headers.get("content-type") ?? "application/json" });
|
|
223
|
+
for (const name of COMPACT_PASSTHROUGH_HEADERS) {
|
|
224
|
+
const value = upstream.headers.get(name);
|
|
225
|
+
if (value) headers.set(name, value);
|
|
226
|
+
}
|
|
227
|
+
return headers;
|
|
228
|
+
}
|
|
229
|
+
|
|
120
230
|
export async function bufferCompactResponse(upstream: Response, signal: AbortSignal): Promise<Response> {
|
|
121
231
|
const reader = upstream.body?.getReader();
|
|
122
|
-
const
|
|
123
|
-
if (!reader) return new Response(null, { status: upstream.status,
|
|
232
|
+
const headers = compactResponseHeaders(upstream);
|
|
233
|
+
if (!reader) return new Response(null, { status: upstream.status, statusText: upstream.statusText, headers });
|
|
124
234
|
const declaredLength = Number(upstream.headers.get("content-length"));
|
|
125
235
|
if (Number.isFinite(declaredLength) && declaredLength > COMPACT_RESPONSE_MAX_BYTES) {
|
|
126
236
|
await reader.cancel("compact_response_too_large").catch(() => undefined);
|
|
@@ -153,7 +263,7 @@ export async function bufferCompactResponse(upstream: Response, signal: AbortSig
|
|
|
153
263
|
body.set(chunk, offset);
|
|
154
264
|
offset += chunk.byteLength;
|
|
155
265
|
}
|
|
156
|
-
return new Response(body, { status: upstream.status,
|
|
266
|
+
return new Response(body, { status: upstream.status, statusText: upstream.statusText, headers });
|
|
157
267
|
}
|
|
158
268
|
|
|
159
269
|
|
|
@@ -162,6 +272,8 @@ export async function handleResponsesCompact(
|
|
|
162
272
|
req: Request,
|
|
163
273
|
config: OcxConfig,
|
|
164
274
|
logCtx: RequestLogContext,
|
|
275
|
+
turnAdmissionLease?: AdmissionLease,
|
|
276
|
+
admission?: DataPlaneAdmission,
|
|
165
277
|
): Promise<Response> {
|
|
166
278
|
let body: unknown;
|
|
167
279
|
try {
|
|
@@ -179,14 +291,32 @@ export async function handleResponsesCompact(
|
|
|
179
291
|
|
|
180
292
|
let route;
|
|
181
293
|
try {
|
|
182
|
-
route
|
|
294
|
+
// Compact requests route through the same policy evaluation as normal
|
|
295
|
+
// turns, so body-derived evidence (tools/image) must reach the first
|
|
296
|
+
// evaluation too - not only the later handleResponses dispatch.
|
|
297
|
+
route = routeModel(config, raw.model, evidenceFromBody(raw));
|
|
183
298
|
} catch (err) {
|
|
299
|
+
if (err instanceof NoEligiblePolicyCandidateError) {
|
|
300
|
+
// Persist the evaluation trace (per-candidate exclusions + the
|
|
301
|
+
// no-eligible reason) so a failed compact policy request stays
|
|
302
|
+
// auditable, matching the other request handlers.
|
|
303
|
+
logCtx.routeDecision = err.trace;
|
|
304
|
+
}
|
|
184
305
|
return formatErrorResponse(404, "invalid_request_error", err instanceof Error ? err.message : String(err));
|
|
185
306
|
}
|
|
186
307
|
const selectedModelId = route.modelId;
|
|
308
|
+
// Derive from the RESOLVED route model, not the caller's raw string. An account-qualified
|
|
309
|
+
// selector like `side/gpt-daybreak-blue-latest` does not match the gated map — `slugsEquivalent`
|
|
310
|
+
// reads the account namespace as a routed provider prefix — so keying on `raw.model` sent
|
|
311
|
+
// exactly the selector form back down the native compact endpoint this guard exists to avoid.
|
|
312
|
+
// `route.modelId` is the same value `applyCodexAccountGatedWireNormalization` uses in core.ts.
|
|
313
|
+
const accountGatedCompactWireModel = codexAccountGatedCanonicalWireModel(selectedModelId);
|
|
187
314
|
logCtx.requestedModel = raw.model;
|
|
188
315
|
logCtx.model = selectedModelId;
|
|
189
|
-
logCtx.
|
|
316
|
+
logCtx.routeDecision = route.routeDecision;
|
|
317
|
+
logCtx.provider = route.codexAccountNamespace
|
|
318
|
+
? `${route.providerName}-${route.codexAccountNamespace}`
|
|
319
|
+
: route.providerName;
|
|
190
320
|
logCtx.providerAdapter = route.provider.adapter;
|
|
191
321
|
const virtual = resolveOpenAiCompactModel(route.providerName, selectedModelId);
|
|
192
322
|
if (virtual) {
|
|
@@ -197,7 +327,13 @@ export async function handleResponsesCompact(
|
|
|
197
327
|
logCtx.resolvedModel = route.modelId;
|
|
198
328
|
}
|
|
199
329
|
|
|
200
|
-
|
|
330
|
+
// #1686: a bearer-presented admission secret is one of ours, so the stored main credential
|
|
331
|
+
// is substituted below instead of the caller bearer being forwarded.
|
|
332
|
+
// #2132: and only when the route is a native Codex one, which is the only route that can
|
|
333
|
+
// consume that credential. See the longer note in core.ts resolveResponsesCodexAuth.
|
|
334
|
+
const substituteMainCredential = admission?.source === "bearer"
|
|
335
|
+
&& route.codexAccountMode !== undefined;
|
|
336
|
+
if (route.codexAccountMode === "direct" && !substituteMainCredential) {
|
|
201
337
|
try { validateForwardAdmissionCredential(req.headers, config); }
|
|
202
338
|
catch (err) {
|
|
203
339
|
if (err instanceof ForwardAdmissionCredentialError) return formatErrorResponse(401, "authentication_error", err.message);
|
|
@@ -208,18 +344,45 @@ export async function handleResponsesCompact(
|
|
|
208
344
|
// Native /responses/compact exists on the canonical ChatGPT backend and on the
|
|
209
345
|
// official OpenAI API. Any other Responses-shaped gateway must take the routed
|
|
210
346
|
// summarizer path below, or compaction fails against an endpoint it never had (#422).
|
|
211
|
-
if (supportsNativeResponsesCompactEndpoint(route.providerName, route.provider)) {
|
|
347
|
+
if (supportsNativeResponsesCompactEndpoint(route.providerName, route.provider) && !accountGatedCompactWireModel) {
|
|
348
|
+
if (req.signal.aborted) {
|
|
349
|
+
return formatErrorResponse(499, "client_cancelled", "Client cancelled compact request");
|
|
350
|
+
}
|
|
351
|
+
// The enclosing native-compact guard already restricts this path to
|
|
352
|
+
// supported backends, so compact intentionally does not require the
|
|
353
|
+
// regular Responses adapter check here.
|
|
354
|
+
const preAuthCompactHostKey = preAuthUpstreamHostCircuitKey(route, config, {
|
|
355
|
+
requireResponsesAdapter: false,
|
|
356
|
+
});
|
|
357
|
+
let compactHostAdmissionLease: UpstreamHostAdmissionLease | null = null;
|
|
358
|
+
let authCtx: CodexAuthContext = { kind: "main", accountId: null };
|
|
359
|
+
if (preAuthCompactHostKey) {
|
|
360
|
+
const admission = acquireUpstreamHostAdmission(
|
|
361
|
+
preAuthCompactHostKey,
|
|
362
|
+
config.upstreamHostCircuitThreshold,
|
|
363
|
+
);
|
|
364
|
+
if (admission.kind === "blocked") {
|
|
365
|
+
return upstreamHostCircuitOpenResponse(admission.retryAfterSeconds);
|
|
366
|
+
}
|
|
367
|
+
compactHostAdmissionLease = admission.lease;
|
|
368
|
+
}
|
|
369
|
+
try {
|
|
212
370
|
// Native ChatGPT/OpenAI model: forward the compact request verbatim to the real backend.
|
|
213
371
|
// Resolve the SAME pool/thread auth context as /v1/responses — forwarding the caller's raw
|
|
214
372
|
// headers would run compaction on the wrong account (or 401) whenever a pool account is
|
|
215
373
|
// active for this thread while normal turns succeed.
|
|
216
374
|
let compactProvider = route.provider;
|
|
217
|
-
let authCtx: CodexAuthContext = { kind: "main", accountId: null };
|
|
218
375
|
const headers = new Headers({ "content-type": "application/json" });
|
|
219
376
|
try {
|
|
220
377
|
if (route.codexAccountMode) {
|
|
221
|
-
authCtx = await resolveCodexAuthContext(req.headers, config, route.codexAccountMode, {
|
|
222
|
-
|
|
378
|
+
authCtx = await resolveCodexAuthContext(req.headers, config, route.codexAccountMode, {
|
|
379
|
+
accountId: route.codexAccountId,
|
|
380
|
+
modelId: selectedModelId,
|
|
381
|
+
substituteMainCredentialForDirect: substituteMainCredential,
|
|
382
|
+
beginCodexAccountSelection: codexAccountSelectionForTurn(turnAdmissionLease),
|
|
383
|
+
});
|
|
384
|
+
logCtx.accountLogLabel = codexAuthContextLogLabel(authCtx, config);
|
|
385
|
+
const selected = materializeCodexUpstreamAuth(req.headers, authCtx, { substituteMainCredential });
|
|
223
386
|
compactProvider = applyCodexAuthContextToProvider(route.provider, authCtx, route.codexAccountMode);
|
|
224
387
|
for (const name of FORWARD_HEADERS) {
|
|
225
388
|
const value = selected.get(name);
|
|
@@ -233,8 +396,9 @@ export async function handleResponsesCompact(
|
|
|
233
396
|
}
|
|
234
397
|
} catch (err) {
|
|
235
398
|
if (err instanceof CodexAccountCooldownError) {
|
|
236
|
-
return cooldownErrorResponse(err);
|
|
399
|
+
return cooldownErrorResponse(err, Date.now(), route.codexAccountNamespace);
|
|
237
400
|
}
|
|
401
|
+
if (err instanceof CodexMainProfileDrainingError) return codexMainProfileDrainingResponse();
|
|
238
402
|
if (err instanceof CodexThreadAffinityExpiredError) {
|
|
239
403
|
return formatErrorResponse(409, "invalid_request_error", "Codex thread account affinity expired; start a new session");
|
|
240
404
|
}
|
|
@@ -246,60 +410,239 @@ export async function handleResponsesCompact(
|
|
|
246
410
|
}
|
|
247
411
|
throw err;
|
|
248
412
|
}
|
|
249
|
-
const base = (compactProvider
|
|
250
|
-
|
|
413
|
+
const base = isCanonicalOpenAiForwardProvider(compactProvider)
|
|
414
|
+
? CODEX_FORWARD_BASE_URL
|
|
415
|
+
: (compactProvider.baseUrl ?? "").replace(/\/+$/, "");
|
|
416
|
+
if (compactProvider.authMode !== "forward" && compactProvider.apiKey) {
|
|
417
|
+
headers.set("authorization", `Bearer ${resolveEnvValue(compactProvider.apiKey)}`);
|
|
418
|
+
}
|
|
251
419
|
const { reasoning: _reasoning, ...compactBodyRaw } = raw as typeof raw & { reasoning?: unknown };
|
|
252
420
|
// The regular /v1/responses path applies sanitizeReasoningInputContent via the adapter's
|
|
253
421
|
// buildRequest, but the compact endpoint forwards directly. Apply the same sanitizer here
|
|
254
422
|
// so routed-model reasoning items (reasoning_text content) don't 400 the ChatGPT backend.
|
|
255
423
|
const compactBody = sanitizeReasoningInputContent(compactBodyRaw) as typeof compactBodyRaw;
|
|
256
424
|
const compactUrl = `${base}/responses/compact`;
|
|
257
|
-
const
|
|
425
|
+
const actualCompactHostKey = upstreamHostHealthKey(
|
|
426
|
+
route.providerName,
|
|
427
|
+
safeOriginLabel(compactUrl),
|
|
428
|
+
);
|
|
429
|
+
const compactHostKey = compactProvider.authMode === "forward"
|
|
430
|
+
? actualCompactHostKey
|
|
431
|
+
: null;
|
|
432
|
+
const compactHostCircuitEnabled = compactHostKey !== null
|
|
433
|
+
&& normalizeUpstreamHostCircuitThreshold(config.upstreamHostCircuitThreshold) > 0;
|
|
434
|
+
if (compactHostKey !== null && !compactHostCircuitEnabled) {
|
|
435
|
+
disableUpstreamHostCircuitForKey(actualCompactHostKey);
|
|
436
|
+
}
|
|
437
|
+
if (compactHostAdmissionLease && compactHostAdmissionLease.key !== compactHostKey) {
|
|
438
|
+
releaseCodexAuthContextProbeLease(authCtx);
|
|
439
|
+
return formatErrorResponse(502, "upstream_error", "Provider host changed after circuit admission");
|
|
440
|
+
}
|
|
441
|
+
if (req.signal.aborted) {
|
|
442
|
+
releaseCodexAuthContextProbeLease(authCtx);
|
|
443
|
+
return formatErrorResponse(499, "client_cancelled", "Client cancelled compact request");
|
|
444
|
+
}
|
|
445
|
+
if (!compactHostAdmissionLease && compactHostCircuitEnabled) {
|
|
446
|
+
const admission = acquireUpstreamHostAdmission(
|
|
447
|
+
compactHostKey!,
|
|
448
|
+
config.upstreamHostCircuitThreshold,
|
|
449
|
+
);
|
|
450
|
+
if (admission.kind === "blocked") {
|
|
451
|
+
releaseCodexAuthContextProbeLease(authCtx);
|
|
452
|
+
return upstreamHostCircuitOpenResponse(admission.retryAfterSeconds);
|
|
453
|
+
}
|
|
454
|
+
compactHostAdmissionLease = admission.lease;
|
|
455
|
+
}
|
|
456
|
+
const settleObservedCompactHostResponse = (): void => {
|
|
457
|
+
if (compactHostCircuitEnabled) {
|
|
458
|
+
resetUpstreamHostHealth(actualCompactHostKey, compactHostAdmissionLease);
|
|
459
|
+
} else {
|
|
460
|
+
resetUpstreamHostHealth(actualCompactHostKey);
|
|
461
|
+
}
|
|
462
|
+
compactHostAdmissionLease = null;
|
|
463
|
+
};
|
|
258
464
|
const connectMs = config.connectTimeoutMs ?? 200_000;
|
|
465
|
+
// Takes its context explicitly: the alternate-account flow below records a rejection
|
|
466
|
+
// against A while promoting B, then records B's own outcome. A closure over a single
|
|
467
|
+
// `authCtx` cannot express either.
|
|
259
468
|
const recordCompactPoolOutcome = (
|
|
469
|
+
ctx: CodexAuthContext,
|
|
260
470
|
outcome: CodexUpstreamOutcome,
|
|
261
|
-
meta: {
|
|
471
|
+
meta: {
|
|
472
|
+
retryAfter?: string | null;
|
|
473
|
+
resetAt?: unknown | unknown[];
|
|
474
|
+
promoteAccountId?: string;
|
|
475
|
+
} = {},
|
|
262
476
|
) => {
|
|
263
|
-
if (!usesCodexForwardPoolAuth(
|
|
264
|
-
recordCodexUpstreamOutcome(config,
|
|
477
|
+
if (!usesCodexForwardPoolAuth(ctx, route.provider)) return;
|
|
478
|
+
recordCodexUpstreamOutcome(config, ctx.accountId, outcome, {
|
|
265
479
|
...meta,
|
|
266
|
-
threadId:
|
|
480
|
+
threadId: ctx.kind === "pool" || ctx.kind === "main-pool" ? ctx.affinityKey : undefined,
|
|
481
|
+
fixedAccount: ctx.fixedAccount,
|
|
267
482
|
modelId: selectedModelId,
|
|
268
|
-
probeLeaseId: codexProbeLeaseId(
|
|
269
|
-
probeQuotaScope: codexProbeQuotaScope(
|
|
270
|
-
writerGeneration:
|
|
271
|
-
?
|
|
483
|
+
probeLeaseId: codexProbeLeaseId(ctx),
|
|
484
|
+
probeQuotaScope: codexProbeQuotaScope(ctx),
|
|
485
|
+
writerGeneration: ctx.kind === "pool" || ctx.kind === "main-pool"
|
|
486
|
+
? ctx.writerGeneration
|
|
272
487
|
: undefined,
|
|
273
488
|
});
|
|
274
489
|
};
|
|
490
|
+
// Two recovery modes, mirroring retryCodexPoolOnAlternateAccount() on the regular
|
|
491
|
+
// path (core.ts:396). The first account keeps the full ladder — transient-5xx retry
|
|
492
|
+
// wrapping reset retry — because those retries happen before any alternate is even
|
|
493
|
+
// considered. The alternate is one bounded send: a second ladder would multiply the
|
|
494
|
+
// work an already-rejecting pool is doing.
|
|
495
|
+
const sendCompactAttempt = (
|
|
496
|
+
sendProvider: OcxProviderConfig,
|
|
497
|
+
sendHeaders: Headers,
|
|
498
|
+
recovery: "normal" | "single",
|
|
499
|
+
): Promise<Response> => {
|
|
500
|
+
const doFetch = (upstreamRecovery?: UpstreamSendRecovery) => fetchWithHeaderTimeout(
|
|
501
|
+
compactUrl,
|
|
502
|
+
applyUpstreamRecoveryInit({
|
|
503
|
+
method: "POST",
|
|
504
|
+
headers: sendHeaders,
|
|
505
|
+
body: JSON.stringify({ ...compactBody, model: route.modelId }),
|
|
506
|
+
}, upstreamRecovery),
|
|
507
|
+
req.signal,
|
|
508
|
+
connectMs,
|
|
509
|
+
false,
|
|
510
|
+
providerFetch(sendProvider, undefined, {
|
|
511
|
+
providerName: route.providerName,
|
|
512
|
+
modelId: route.modelId,
|
|
513
|
+
}),
|
|
514
|
+
// Every credential-bearing forward send gets manual redirects, not only
|
|
515
|
+
// pool sends: direct mode carries the caller's credential too (#914).
|
|
516
|
+
sendProvider.authMode === "forward",
|
|
517
|
+
).then(res => {
|
|
518
|
+
// Every real attempt response — including an intermediate 5xx the retry
|
|
519
|
+
// wrapper replaces — proves the host was reached (#914 review).
|
|
520
|
+
settleObservedCompactHostResponse();
|
|
521
|
+
return res;
|
|
522
|
+
});
|
|
523
|
+
return recovery === "single"
|
|
524
|
+
? doFetch()
|
|
525
|
+
: fetchWithTransientRetry(doFetch, { abortSignal: req.signal, label: safeHostLabel(compactUrl) });
|
|
526
|
+
};
|
|
527
|
+
|
|
528
|
+
// The account each outcome belongs to. Reassigned only when the alternate send below
|
|
529
|
+
// actually happens, so every recorder call names the context that produced it.
|
|
530
|
+
let outcomeCtx = authCtx;
|
|
275
531
|
let upstream: Response;
|
|
276
532
|
try {
|
|
277
533
|
// Same connect timeout + keep-alive reset + transient-5xx recovery as /v1/responses —
|
|
278
534
|
// compact hits the same ChatGPT host and must soft-avoid / clear affinity (#186).
|
|
279
|
-
upstream = await
|
|
280
|
-
recovery => fetchWithHeaderTimeout(
|
|
281
|
-
compactUrl,
|
|
282
|
-
applyUpstreamRecoveryInit({
|
|
283
|
-
method: "POST",
|
|
284
|
-
headers,
|
|
285
|
-
body: JSON.stringify({ ...compactBody, model: route.modelId }),
|
|
286
|
-
}, recovery),
|
|
287
|
-
req.signal,
|
|
288
|
-
connectMs,
|
|
289
|
-
false,
|
|
290
|
-
providerFetch(compactProvider),
|
|
291
|
-
),
|
|
292
|
-
{ abortSignal: req.signal, label: safeHostLabel(compactUrl) },
|
|
293
|
-
);
|
|
535
|
+
upstream = await sendCompactAttempt(compactProvider, headers, "normal");
|
|
294
536
|
} catch (err) {
|
|
295
537
|
if (req.signal.aborted) {
|
|
296
|
-
recordCompactPoolOutcome(499);
|
|
538
|
+
recordCompactPoolOutcome(outcomeCtx, 499);
|
|
297
539
|
return formatErrorResponse(499, "client_cancelled", "Client cancelled compact request");
|
|
298
540
|
}
|
|
299
|
-
const outcome = err
|
|
300
|
-
|
|
541
|
+
const outcome = classifyTransportFailureKind(err);
|
|
542
|
+
// Host-level evidence stands regardless of pool membership (#914 review).
|
|
543
|
+
if (outcome === "connect_neutral") {
|
|
544
|
+
if (compactHostCircuitEnabled) {
|
|
545
|
+
recordUpstreamHostFailure(actualCompactHostKey, {
|
|
546
|
+
code: transportErrorCode(err),
|
|
547
|
+
threshold: config.upstreamHostCircuitThreshold,
|
|
548
|
+
lease: compactHostAdmissionLease,
|
|
549
|
+
});
|
|
550
|
+
} else {
|
|
551
|
+
recordUpstreamHostFailure(actualCompactHostKey, { code: transportErrorCode(err) });
|
|
552
|
+
}
|
|
553
|
+
} else {
|
|
554
|
+
releaseUpstreamHostAdmission(compactHostAdmissionLease);
|
|
555
|
+
}
|
|
556
|
+
compactHostAdmissionLease = null;
|
|
557
|
+
recordCompactPoolOutcome(outcomeCtx, outcome);
|
|
301
558
|
return formatErrorResponse(502, "upstream_error", "Failed to connect to compact upstream");
|
|
302
559
|
}
|
|
560
|
+
|
|
561
|
+
// Bounded same-request alternate: the regular /v1/responses path already does this
|
|
562
|
+
// (core.ts:319-423) and recognizes exactly 429/402. Without it a pool rejection
|
|
563
|
+
// surfaces to the client, which retries the compact task OUTSIDE the logical request
|
|
564
|
+
// — reporting exhausted retries while another pool account sat idle (#913).
|
|
565
|
+
if (
|
|
566
|
+
(upstream.status === 429 || upstream.status === 402)
|
|
567
|
+
&& usesCodexForwardPoolAuth(authCtx, route.provider)
|
|
568
|
+
&& !authCtx.fixedAccount
|
|
569
|
+
&& route.codexAccountMode
|
|
570
|
+
&& !req.signal.aborted
|
|
571
|
+
) {
|
|
572
|
+
const firstRetryAfter = upstream.headers.get("retry-after");
|
|
573
|
+
const firstResetAt = [
|
|
574
|
+
upstream.headers.get("x-codex-primary-reset-at"),
|
|
575
|
+
upstream.headers.get("x-codex-secondary-reset-at"),
|
|
576
|
+
upstream.headers.get("x-codex-tertiary-reset-at"),
|
|
577
|
+
].filter(Boolean);
|
|
578
|
+
// Build the alternate COMPLETELY before cancelling the first body: if construction
|
|
579
|
+
// throws, the first rejection is still intact and can be returned to the client.
|
|
580
|
+
const alternate = await resolveAlternateCompactContext({
|
|
581
|
+
req,
|
|
582
|
+
config,
|
|
583
|
+
route,
|
|
584
|
+
selectedModelId,
|
|
585
|
+
excludeAccountId: authCtx.accountId,
|
|
586
|
+
turnAdmissionLease,
|
|
587
|
+
});
|
|
588
|
+
// Resolution can await a credential refresh, so the client may have gone away
|
|
589
|
+
// while we were choosing B. Re-check before spending anything: recording A,
|
|
590
|
+
// cancelling its body, and sending B are all observable side effects, and B's
|
|
591
|
+
// quota is not ours to spend on a request nobody is waiting for.
|
|
592
|
+
if (alternate && req.signal.aborted) {
|
|
593
|
+
releaseCodexAuthContextProbeLease(alternate.authCtx);
|
|
594
|
+
recordCompactPoolOutcome(outcomeCtx, 499);
|
|
595
|
+
return formatErrorResponse(499, "client_cancelled", "Client cancelled compact request");
|
|
596
|
+
}
|
|
597
|
+
if (alternate) {
|
|
598
|
+
// Same order the regular path uses (core.ts:349-357): a 429/402 carries the
|
|
599
|
+
// quota snapshot that produced it, so refresh A's cache before recording its
|
|
600
|
+
// rejection. Skipping this leaves quota-strategy routing and the dashboard
|
|
601
|
+
// reading numbers from before the account ran out.
|
|
602
|
+
if (authCtx.kind === "pool" || authCtx.kind === "main-pool") {
|
|
603
|
+
const { applyAccountQuotaFromUpstreamHeaders } = await import("../../codex/auth-api");
|
|
604
|
+
applyAccountQuotaFromUpstreamHeaders(
|
|
605
|
+
authCtx.accountId,
|
|
606
|
+
upstream.headers,
|
|
607
|
+
authCtx.writerGeneration,
|
|
608
|
+
);
|
|
609
|
+
}
|
|
610
|
+
recordCompactPoolOutcome(authCtx, upstream.status, {
|
|
611
|
+
retryAfter: firstRetryAfter,
|
|
612
|
+
resetAt: firstResetAt,
|
|
613
|
+
...(alternate.authCtx.accountId ? { promoteAccountId: alternate.authCtx.accountId } : {}),
|
|
614
|
+
});
|
|
615
|
+
await upstream.body?.cancel().catch(() => undefined);
|
|
616
|
+
outcomeCtx = alternate.authCtx;
|
|
617
|
+
logCtx.accountLogLabel = codexAuthContextLogLabel(alternate.authCtx, config);
|
|
618
|
+
try {
|
|
619
|
+
upstream = await sendCompactAttempt(alternate.provider, alternate.headers, "single");
|
|
620
|
+
} catch (err) {
|
|
621
|
+
if (req.signal.aborted) {
|
|
622
|
+
recordCompactPoolOutcome(outcomeCtx, 499);
|
|
623
|
+
return formatErrorResponse(499, "client_cancelled", "Client cancelled compact request");
|
|
624
|
+
}
|
|
625
|
+
const outcome = classifyTransportFailureKind(err);
|
|
626
|
+
// Host-level evidence stands regardless of pool membership (#914 review).
|
|
627
|
+
if (outcome === "connect_neutral") {
|
|
628
|
+
if (compactHostCircuitEnabled) {
|
|
629
|
+
recordUpstreamHostFailure(actualCompactHostKey, {
|
|
630
|
+
code: transportErrorCode(err),
|
|
631
|
+
threshold: config.upstreamHostCircuitThreshold,
|
|
632
|
+
lease: compactHostAdmissionLease,
|
|
633
|
+
});
|
|
634
|
+
} else {
|
|
635
|
+
recordUpstreamHostFailure(actualCompactHostKey, { code: transportErrorCode(err) });
|
|
636
|
+
}
|
|
637
|
+
} else {
|
|
638
|
+
releaseUpstreamHostAdmission(compactHostAdmissionLease);
|
|
639
|
+
}
|
|
640
|
+
compactHostAdmissionLease = null;
|
|
641
|
+
recordCompactPoolOutcome(outcomeCtx, outcome);
|
|
642
|
+
return formatErrorResponse(502, "upstream_error", "Failed to connect to compact upstream");
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
}
|
|
303
646
|
const retryAfter = upstream.headers.get("retry-after");
|
|
304
647
|
const resetAt = [
|
|
305
648
|
upstream.headers.get("x-codex-primary-reset-at"),
|
|
@@ -310,13 +653,21 @@ export async function handleResponsesCompact(
|
|
|
310
653
|
// Record pool health only after the body is fully delivered (or definitively failed).
|
|
311
654
|
// A premature 200 would clear soft-avoid while the client still sees a buffer 502.
|
|
312
655
|
if (buffered.status === 499) {
|
|
313
|
-
recordCompactPoolOutcome(499);
|
|
656
|
+
recordCompactPoolOutcome(outcomeCtx, 499);
|
|
314
657
|
return buffered;
|
|
315
658
|
}
|
|
316
659
|
// Always record the real upstream status: a local buffering failure after a
|
|
317
660
|
// 200 upstream response must not soft-avoid a healthy account or rotate a thread.
|
|
318
|
-
recordCompactPoolOutcome(upstream.status, { retryAfter, resetAt });
|
|
661
|
+
recordCompactPoolOutcome(outcomeCtx, upstream.status, { retryAfter, resetAt });
|
|
662
|
+
// Lift usage and response metadata from the buffered upstream JSON into the
|
|
663
|
+
// request log; the routed branch gets the same through handleResponses. The
|
|
664
|
+
// synthetic buffer errors are not upstream bodies and stay uninspected.
|
|
665
|
+
if (buffered.ok) inspectResponseLogJson(logCtx, await buffered.clone().text());
|
|
319
666
|
return buffered;
|
|
667
|
+
} finally {
|
|
668
|
+
releaseUpstreamHostAdmission(compactHostAdmissionLease);
|
|
669
|
+
releaseCodexAuthContextProbeLease(authCtx);
|
|
670
|
+
}
|
|
320
671
|
}
|
|
321
672
|
|
|
322
673
|
// ROUTED model: run the v2 synthetic-compaction turn internally (appends COMPACT_PROMPT, no
|
|
@@ -324,7 +675,10 @@ export async function handleResponsesCompact(
|
|
|
324
675
|
const inputItems = Array.isArray(raw.input) ? (raw.input as unknown[]) : [];
|
|
325
676
|
const internalBody = {
|
|
326
677
|
...raw,
|
|
327
|
-
|
|
678
|
+
// Canonical ChatGPT Responses rejects non-streaming turns. Daybreak cannot use the
|
|
679
|
+
// native compact endpoint either, so run its synthetic compaction as SSE and collapse
|
|
680
|
+
// the completed event back into the v1 compact JSON contract below.
|
|
681
|
+
stream: accountGatedCompactWireModel ? true : false,
|
|
328
682
|
input: [...inputItems, { type: "compaction_trigger" }],
|
|
329
683
|
};
|
|
330
684
|
const internalHeaders = new Headers({ "content-type": "application/json" });
|
|
@@ -337,13 +691,39 @@ export async function handleResponsesCompact(
|
|
|
337
691
|
headers: internalHeaders,
|
|
338
692
|
body: JSON.stringify(internalBody),
|
|
339
693
|
});
|
|
340
|
-
const response = await handleResponses(internalReq, config, logCtx, { abortSignal: req.signal });
|
|
694
|
+
const response = await handleResponses(internalReq, config, logCtx, { abortSignal: req.signal, turnAdmissionLease, ...(admission ? { admission } : {}) });
|
|
341
695
|
if (!response.ok) return response;
|
|
342
696
|
let json: { output?: unknown[]; status?: unknown; error?: unknown };
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
697
|
+
if (response.headers.get("content-type")?.includes("text/event-stream")) {
|
|
698
|
+
if (!response.body) {
|
|
699
|
+
return formatErrorResponse(502, "server_error", "compaction turn returned an empty event stream");
|
|
700
|
+
}
|
|
701
|
+
const terminal = { status: "incomplete" as "completed" | "failed" | "incomplete" };
|
|
702
|
+
let completed: { id?: unknown; output?: unknown; status?: unknown } | undefined;
|
|
703
|
+
await new Promise<void>(resolve => {
|
|
704
|
+
consumeForInspection(
|
|
705
|
+
response.body!,
|
|
706
|
+
status => { terminal.status = status; },
|
|
707
|
+
req.signal,
|
|
708
|
+
resolve,
|
|
709
|
+
undefined,
|
|
710
|
+
undefined,
|
|
711
|
+
value => { completed = value; },
|
|
712
|
+
);
|
|
713
|
+
});
|
|
714
|
+
if (req.signal.aborted) {
|
|
715
|
+
return formatErrorResponse(499, "client_cancelled", "Client cancelled compact request");
|
|
716
|
+
}
|
|
717
|
+
if (terminal.status !== "completed" || !completed) {
|
|
718
|
+
return formatErrorResponse(502, "upstream_error", `compaction turn did not complete (status: ${terminal.status})`);
|
|
719
|
+
}
|
|
720
|
+
json = completed as { output?: unknown[]; status?: unknown; error?: unknown };
|
|
721
|
+
} else {
|
|
722
|
+
try {
|
|
723
|
+
json = await response.json() as { output?: unknown[]; status?: unknown; error?: unknown };
|
|
724
|
+
} catch {
|
|
725
|
+
return formatErrorResponse(502, "server_error", "compaction turn returned a non-JSON response");
|
|
726
|
+
}
|
|
347
727
|
}
|
|
348
728
|
// The internal turn answers 200 even when it failed or was truncated, so the body
|
|
349
729
|
// has to be inspected. Reporting a failure beats installing "(no summary
|
|
@@ -372,6 +752,13 @@ export async function handleResponsesCompact(
|
|
|
372
752
|
`compaction turn produced ${compactionItems.length} compaction items, expected exactly 1`,
|
|
373
753
|
);
|
|
374
754
|
}
|
|
755
|
+
// The canonical Responses stream returns a real OpenAI-encrypted compaction item. OCX cannot
|
|
756
|
+
// and should not decrypt it; /responses/compact callers can consume that item directly.
|
|
757
|
+
if (accountGatedCompactWireModel) {
|
|
758
|
+
return new Response(JSON.stringify({ output: compactionItems }), {
|
|
759
|
+
headers: { "Content-Type": "application/json" },
|
|
760
|
+
});
|
|
761
|
+
}
|
|
375
762
|
const encrypted = compactionItems[0]!.encrypted_content;
|
|
376
763
|
const decoded = typeof encrypted === "string" ? decodeCompactionSummary(encrypted) : null;
|
|
377
764
|
// An empty `ocx1:` envelope decodes to "" rather than null, so length is what matters.
|