@iislee/opencodex 2.11.0 → 2.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS_INSTALL.md +109 -0
- package/README.md +117 -22
- package/bin/ocx.mjs +245 -88
- package/bin/package-main.mjs +1 -1
- package/gui/dist/assets/index-Be1sDpWw.css +1 -0
- package/gui/dist/assets/index-D4OKgUV4.js +114 -0
- package/gui/dist/index.html +2 -2
- package/gui/dist/provider-icons/alibaba-color.svg +1 -1
- package/gui/dist/provider-icons/antigravity-color.svg +1 -1
- package/gui/dist/provider-icons/claude-color.svg +1 -1
- package/gui/dist/provider-icons/cline-color.svg +16 -0
- package/gui/dist/provider-icons/cloudflare-ai-gateway-color.svg +1 -1
- package/gui/dist/provider-icons/commandcode-color.svg +1 -0
- package/gui/dist/provider-icons/copilot-color.svg +1 -1
- package/gui/dist/provider-icons/cursor-color.svg +1 -1
- package/gui/dist/provider-icons/deepseek-color.svg +1 -1
- package/gui/dist/provider-icons/firepass-color.svg +1 -1
- package/gui/dist/provider-icons/fireworks-color.svg +1 -1
- package/gui/dist/provider-icons/gemini-color.svg +1 -1
- package/gui/dist/provider-icons/github-copilot-color.svg +1 -1
- package/gui/dist/provider-icons/gitlab-duo-color.svg +1 -1
- package/gui/dist/provider-icons/grok.svg +1 -1
- package/gui/dist/provider-icons/groq-color.svg +1 -1
- package/gui/dist/provider-icons/huggingface-color.svg +1 -1
- package/gui/dist/provider-icons/kimi-color.svg +1 -1
- package/gui/dist/provider-icons/kiro-color.svg +2 -2
- package/gui/dist/provider-icons/lm-studio-color.svg +1 -1
- package/gui/dist/provider-icons/mistral-color.svg +1 -1
- package/gui/dist/provider-icons/moonshot-color.svg +1 -1
- package/gui/dist/provider-icons/nvidia-color.svg +1 -1
- package/gui/dist/provider-icons/ollama-color.svg +1 -1
- package/gui/dist/provider-icons/openai.svg +1 -1
- package/gui/dist/provider-icons/opencode.svg +2 -1
- package/gui/dist/provider-icons/openrouter-color.svg +1 -1
- package/gui/dist/provider-icons/pi.svg +2 -2
- package/gui/dist/provider-icons/qianfan-color.svg +1 -1
- package/gui/dist/provider-icons/qwen-portal-color.svg +1 -1
- package/gui/dist/provider-icons/vercel-ai-gateway-color.svg +1 -1
- package/gui/dist/provider-icons/vllm-color.svg +1 -1
- package/gui/dist/provider-icons/xiaomi-color.svg +1 -1
- package/package.json +21 -10
- package/src/adapters/anthropic-output-schema.ts +137 -0
- package/src/adapters/anthropic.ts +475 -62
- package/src/adapters/base.ts +82 -7
- package/src/adapters/client-fingerprint.ts +18 -12
- package/src/adapters/cline-pass-deepseek-v4-tool-replay.ts +69 -0
- package/src/adapters/command-code.ts +637 -0
- package/src/adapters/cursor/call-id.ts +44 -0
- package/src/adapters/cursor/catalog.ts +541 -0
- package/src/adapters/cursor/checkpoint-store.ts +308 -0
- package/src/adapters/cursor/cursor-errors.ts +144 -6
- package/src/adapters/cursor/discovery.ts +122 -14
- package/src/adapters/cursor/effort-map.ts +106 -4
- package/src/adapters/cursor/envelope-echo.ts +290 -0
- package/src/adapters/cursor/framing.ts +39 -0
- package/src/adapters/cursor/h2-pool.ts +123 -0
- package/src/adapters/cursor/http1-bidi.ts +361 -0
- package/src/adapters/cursor/images.ts +704 -0
- package/src/adapters/cursor/live-models.ts +180 -59
- package/src/adapters/cursor/live-transport.ts +623 -170
- package/src/adapters/cursor/message-mapper.ts +4 -1
- package/src/adapters/cursor/native-exec-common.ts +23 -2
- package/src/adapters/cursor/native-exec-desktop.ts +23 -0
- package/src/adapters/cursor/native-exec-fs.ts +10 -7
- package/src/adapters/cursor/native-exec-network.ts +1 -1
- package/src/adapters/cursor/native-exec-shell.ts +5 -3
- package/src/adapters/cursor/native-exec.ts +121 -14
- package/src/adapters/cursor/protobuf-events.ts +829 -11
- package/src/adapters/cursor/protobuf-request.ts +447 -75
- package/src/adapters/cursor/request-builder.ts +271 -35
- package/src/adapters/cursor/tool-definitions.ts +252 -12
- package/src/adapters/cursor/tool-result-normalize.ts +115 -0
- package/src/adapters/cursor/transport.ts +22 -0
- package/src/adapters/cursor/types.ts +48 -1
- package/src/adapters/cursor.ts +336 -39
- package/src/adapters/exec-tool-result-normalize.ts +99 -0
- package/src/adapters/google-antigravity-replay.ts +713 -41
- package/src/adapters/google-antigravity-wire.ts +48 -10
- package/src/adapters/google-errors.ts +44 -12
- package/src/adapters/google-http.ts +38 -10
- package/src/adapters/google-tool-schema.ts +80 -15
- package/src/adapters/google-truncation.ts +11 -0
- package/src/adapters/google.ts +742 -86
- package/src/adapters/identity.ts +39 -6
- package/src/adapters/image.ts +1 -1
- package/src/adapters/kiro-constants.ts +27 -0
- package/src/adapters/kiro-errors.ts +11 -0
- package/src/adapters/kiro-events.ts +19 -1
- package/src/adapters/kiro-thinking.ts +18 -2
- package/src/adapters/kiro-tools.ts +65 -17
- package/src/adapters/kiro.ts +270 -73
- package/src/adapters/mimo-free.ts +17 -0
- package/src/adapters/openai-chat-url.ts +11 -0
- package/src/adapters/openai-chat.ts +1377 -344
- package/src/adapters/openai-responses-url.ts +16 -0
- package/src/adapters/openai-responses.ts +1164 -64
- package/src/adapters/registry.ts +175 -0
- package/src/adapters/responses-tool-schema.ts +67 -0
- package/src/adapters/run-turn-queue.ts +36 -1
- package/src/adapters/tool-call-id.ts +119 -0
- package/src/adapters/tool-catalog-nudge.ts +105 -21
- package/src/adapters/xai-tool-schema.ts +436 -0
- package/src/adapters/xai-web-search.ts +186 -0
- package/src/bridge.ts +512 -92
- package/src/chat/inbound.ts +57 -20
- package/src/chat/outbound.ts +97 -33
- package/src/claude/agents-inject.ts +40 -10
- package/src/claude/context-windows.ts +37 -14
- package/src/claude/desktop-3p.ts +243 -9
- package/src/claude/gateway-cache.ts +41 -4
- package/src/claude/inbound.ts +72 -3
- package/src/claude/model-info.ts +38 -15
- package/src/claude/outbound.ts +88 -21
- package/src/cli/access.ts +46 -3
- package/src/cli/account-api.ts +150 -19
- package/src/cli/account-auth.ts +33 -6
- package/src/cli/account-catalog-refresh.ts +14 -0
- package/src/cli/account-extended.ts +648 -28
- package/src/cli/account-main.ts +317 -0
- package/src/cli/account.ts +97 -19
- package/src/cli/agent.ts +53 -2
- package/src/cli/alias.ts +66 -0
- package/src/cli/capabilities-command.ts +94 -0
- package/src/cli/capabilities.ts +496 -0
- package/src/cli/claude-agent-startup-sync.ts +73 -0
- package/src/cli/claude-desktop.ts +60 -15
- package/src/cli/claude.ts +129 -35
- package/src/cli/codex-log-guard-doctor.ts +103 -0
- package/src/cli/codex-shim-autorestore.ts +2 -0
- package/src/cli/codex-shim-readiness.ts +76 -0
- package/src/cli/combo.ts +8 -0
- package/src/cli/config-command.ts +74 -10
- package/src/cli/dispatch.ts +821 -0
- package/src/cli/doctor.ts +440 -56
- package/src/cli/ensure-desired-integrations.ts +152 -0
- package/src/cli/export-command.ts +46 -20
- package/src/cli/help.ts +34 -274
- package/src/cli/index.ts +417 -528
- package/src/cli/init.ts +5 -17
- package/src/cli/inspect.ts +230 -0
- package/src/cli/integrations.ts +120 -2
- package/src/cli/lab.ts +607 -0
- package/src/cli/launcher-context.ts +77 -0
- package/src/cli/minimax.ts +497 -0
- package/src/cli/models-runtime.ts +130 -2
- package/src/cli/models.ts +118 -16
- package/src/cli/observe.ts +123 -11
- package/src/cli/opencode.ts +4 -2
- package/src/cli/provider-runtime.ts +34 -3
- package/src/cli/provider.ts +25 -3
- package/src/cli/ready.ts +301 -0
- package/src/cli/registry.ts +476 -0
- package/src/cli/root.ts +86 -0
- package/src/cli/route-policy.ts +92 -0
- package/src/cli/runtime-api.ts +57 -10
- package/src/cli/star-prompt.ts +71 -15
- package/src/cli/status.ts +51 -4
- package/src/cli/storage.ts +234 -0
- package/src/cli/system-command.ts +16 -0
- package/src/cli/system-restart-client.ts +146 -0
- package/src/cli/tray-proxy.ts +153 -6
- package/src/cli/usage-report.ts +184 -0
- package/src/cli/v2.ts +137 -18
- package/src/cli/version-skew.ts +46 -0
- package/src/cli.ts +1 -1
- package/src/clients/config-export.ts +1442 -23
- package/src/clients/effective-status.ts +30 -2
- package/src/clients/probes/cline.ts +81 -0
- package/src/codex/account-label.ts +35 -1
- package/src/codex/account-lifecycle.ts +130 -13
- package/src/codex/account-namespaces.ts +49 -3
- package/src/codex/account-pause.ts +2 -1
- package/src/codex/account-priority.ts +84 -0
- package/src/codex/account-store.ts +29 -2
- package/src/codex/account-usability.ts +25 -2
- package/src/codex/admission.ts +256 -0
- package/src/codex/affinity-debug.ts +162 -0
- package/src/codex/app-server-processes.ts +570 -107
- package/src/codex/app-server-restart-service.ts +232 -0
- package/src/codex/auth-api.ts +893 -246
- package/src/codex/auth-collision.ts +5 -3
- package/src/codex/auth-context.ts +348 -32
- package/src/codex/autostart-health.ts +24 -1
- package/src/codex/catalog/account-models.ts +67 -0
- package/src/codex/catalog/aggregation.ts +86 -10
- package/src/codex/catalog/bundled.ts +331 -33
- package/src/codex/catalog/effort.ts +138 -32
- package/src/codex/catalog/filesystem-evidence.ts +302 -0
- package/src/codex/catalog/kinds.ts +2 -0
- package/src/codex/catalog/metadata.ts +555 -45
- package/src/codex/catalog/native-models.ts +75 -0
- package/src/codex/catalog/parsing.ts +269 -30
- package/src/codex/catalog/provider-fetch.ts +1626 -144
- package/src/codex/catalog/sync.ts +1576 -187
- package/src/codex/catalog-admission.ts +199 -0
- package/src/codex/catalog-refresh-status.ts +105 -0
- package/src/codex/catalog-write-serialization.ts +242 -0
- package/src/codex/catalog.ts +6 -3
- package/src/codex/codex-write-lock.ts +386 -0
- package/src/codex/convergence-types.ts +614 -0
- package/src/codex/convergence.ts +676 -0
- package/src/codex/coordinator-doctor.ts +332 -0
- package/src/codex/custom-model-catalog-migration.ts +176 -0
- package/src/codex/desired-state.ts +230 -0
- package/src/codex/desktop-app-restart.ts +355 -0
- package/src/codex/features.ts +636 -39
- package/src/codex/generation.ts +202 -0
- package/src/codex/history-job.ts +436 -0
- package/src/codex/history-lock.ts +242 -0
- package/src/codex/history-manifest.ts +112 -0
- package/src/codex/history-migration-guardian.ts +30 -24
- package/src/codex/history-provider.ts +1016 -235
- package/src/codex/history-transition.ts +105 -0
- package/src/codex/history-worker.ts +223 -0
- package/src/codex/inject-coordination.ts +373 -0
- package/src/codex/inject.ts +1114 -152
- package/src/codex/injected-marker.ts +37 -3
- package/src/codex/integration-record.ts +266 -0
- package/src/codex/internal/catalog-writer.ts +203 -0
- package/src/codex/internal/history-writer.ts +81 -0
- package/src/codex/journal.ts +66 -4
- package/src/codex/log-guard/inspect.ts +524 -0
- package/src/codex/log-guard/lock.ts +150 -0
- package/src/codex/log-guard/maintenance.ts +403 -0
- package/src/codex/log-guard/path-safety.ts +88 -0
- package/src/codex/log-guard/policy.ts +44 -0
- package/src/codex/log-guard/processes.ts +205 -0
- package/src/codex/log-guard/protection.ts +489 -0
- package/src/codex/log-guard/sqlite-errors.ts +9 -0
- package/src/codex/main-account-cache.ts +24 -0
- package/src/codex/main-account.ts +29 -1
- package/src/codex/management-convergence.ts +167 -0
- package/src/codex/model-cache.ts +56 -10
- package/src/codex/model-entitlements.ts +360 -0
- package/src/codex/native-main-admission.ts +47 -0
- package/src/codex/native-main-auth-temp.ts +187 -0
- package/src/codex/native-main-claim.ts +178 -0
- package/src/codex/native-main-lock-file.ts +162 -0
- package/src/codex/native-main-owner.ts +329 -0
- package/src/codex/native-profile-api.ts +247 -0
- package/src/codex/native-profile-manager.ts +1531 -0
- package/src/codex/native-profile-processes.ts +121 -0
- package/src/codex/native-profile-recovery.ts +99 -0
- package/src/codex/native-profile-stage-store.ts +387 -0
- package/src/codex/native-profile-startup.ts +622 -0
- package/src/codex/native-profile-store.ts +855 -0
- package/src/codex/native-profile-types.ts +120 -0
- package/src/codex/native-residue.ts +675 -0
- package/src/codex/paths.ts +80 -1
- package/src/codex/plan-from-token.ts +140 -0
- package/src/codex/plan.ts +40 -0
- package/src/codex/plugins-doctor.ts +1 -1
- package/src/codex/pool-rotation.ts +74 -4
- package/src/codex/project-config-warnings.ts +20 -6
- package/src/codex/prompt-journal.ts +352 -0
- package/src/codex/prompt-layers.ts +1313 -0
- package/src/codex/prompt-lock.ts +143 -0
- package/src/codex/prompt-text-probe.ts +238 -0
- package/src/codex/quota-rejection.ts +298 -0
- package/src/codex/quota.ts +264 -21
- package/src/codex/refresh.ts +11 -2
- package/src/codex/reset-credit-recovery.ts +1044 -0
- package/src/codex/routing.ts +514 -94
- package/src/codex/runtime.ts +159 -38
- package/src/codex/shim.ts +1065 -31
- package/src/codex/subagent-model-fallback.ts +437 -43
- package/src/codex/sync.ts +191 -2
- package/src/codex/transition-state.ts +720 -0
- package/src/codex/upstream-host-health.ts +368 -0
- package/src/codex/user-identity.ts +557 -0
- package/src/codex/warmup.ts +187 -81
- package/src/codex/write-coordination.ts +114 -0
- package/src/combos/failover.ts +47 -0
- package/src/combos/index.ts +4 -0
- package/src/combos/request.ts +32 -0
- package/src/combos/types.ts +91 -9
- package/src/compatibility/index.ts +26 -0
- package/src/compatibility/manifest.ts +253 -0
- package/src/compatibility/openai-responses.ts +121 -0
- package/src/config/atomic-write.ts +219 -0
- package/src/config/paths.ts +40 -0
- package/src/config/process-state.ts +309 -0
- package/src/config/provider-name.ts +24 -0
- package/src/config/provider-validation.ts +177 -0
- package/src/config/rebase-provenance.ts +68 -0
- package/src/config.ts +1713 -709
- package/src/generated/compatibility-version.json +3324 -0
- package/src/generated/{jawcode-model-metadata.ts → model-metadata.ts} +19 -17
- package/src/grok/inject.ts +16 -5
- package/src/grok/inspect.ts +45 -0
- package/src/grok/sync.ts +2 -2
- package/src/images/loop.ts +157 -33
- package/src/images/plan.ts +24 -13
- package/src/integrations/config-io.ts +269 -0
- package/src/integrations/journal.ts +315 -0
- package/src/integrations/merge.ts +135 -0
- package/src/integrations/mutation-flight.ts +71 -0
- package/src/integrations/native/ownership-preflight.ts +202 -0
- package/src/integrations/omp-yaml-source.ts +358 -0
- package/src/integrations/owned-refresh.ts +74 -0
- package/src/integrations/ownership-policy.ts +160 -0
- package/src/integrations/ownership.ts +155 -0
- package/src/integrations/registry.ts +166 -0
- package/src/integrations/serialize.ts +314 -0
- package/src/integrations/state.ts +433 -0
- package/src/integrations/store.ts +103 -0
- package/src/integrations/writer-lock.ts +98 -0
- package/src/integrations/writer.ts +715 -0
- package/src/lab/artifacts/sanitize.ts +586 -0
- package/src/lab/artifacts/secure-fs.ts +475 -0
- package/src/lab/artifacts/store.ts +310 -0
- package/src/lab/automation/budgets.ts +78 -0
- package/src/lab/automation/config-persistence.ts +256 -0
- package/src/lab/automation/constants.ts +39 -0
- package/src/lab/automation/cooldown.ts +103 -0
- package/src/lab/automation/dispatch.ts +211 -0
- package/src/lab/automation/index.ts +13 -0
- package/src/lab/automation/orchestrator.ts +499 -0
- package/src/lab/automation/persistence.ts +512 -0
- package/src/lab/automation/planner.ts +371 -0
- package/src/lab/automation/policy.ts +136 -0
- package/src/lab/automation/queue.ts +191 -0
- package/src/lab/automation/recovery.ts +24 -0
- package/src/lab/automation/route-context.ts +21 -0
- package/src/lab/automation/run-key.ts +44 -0
- package/src/lab/automation/runs-query.ts +34 -0
- package/src/lab/automation/types.ts +160 -0
- package/src/lab/conformance/assertion.ts +325 -0
- package/src/lab/conformance/digest.ts +22 -0
- package/src/lab/conformance/executor.ts +741 -0
- package/src/lab/conformance/fixture-provider.ts +27 -0
- package/src/lab/conformance/fixtures/live-v1-cases.json +175 -0
- package/src/lab/conformance/fixtures/protocol-v1-cases.json +461 -0
- package/src/lab/conformance/harness-budget.ts +47 -0
- package/src/lab/conformance/index.ts +5 -0
- package/src/lab/conformance/jcs.ts +64 -0
- package/src/lab/conformance/json-pointer.ts +39 -0
- package/src/lab/conformance/manifest.ts +180 -0
- package/src/lab/conformance/mcp-stub.ts +179 -0
- package/src/lab/conformance/negative-controls.ts +164 -0
- package/src/lab/conformance/observation.ts +355 -0
- package/src/lab/conformance/runner.ts +68 -0
- package/src/lab/conformance/sse-normalize.ts +59 -0
- package/src/lab/conformance/suite-manifest.ts +78 -0
- package/src/lab/conformance/types.ts +214 -0
- package/src/lab/constants.ts +126 -0
- package/src/lab/digest.ts +64 -0
- package/src/lab/events/errors.ts +9 -0
- package/src/lab/events/limits.ts +117 -0
- package/src/lab/events/types.ts +229 -0
- package/src/lab/events/validate.ts +781 -0
- package/src/lab/fabric/constants.ts +40 -0
- package/src/lab/fabric/executor.ts +492 -0
- package/src/lab/fabric/index.ts +80 -0
- package/src/lab/fabric/manifest.ts +222 -0
- package/src/lab/fabric/observe.ts +489 -0
- package/src/lab/fabric/patch.ts +79 -0
- package/src/lab/fabric/producer-child.ts +139 -0
- package/src/lab/fabric/producer-isolate.ts +276 -0
- package/src/lab/fabric/producer-protocol.ts +61 -0
- package/src/lab/fabric/scratch.ts +439 -0
- package/src/lab/fabric/subject.ts +106 -0
- package/src/lab/fabric/types.ts +134 -0
- package/src/lab/fabric/verifier.ts +98 -0
- package/src/lab/index.ts +54 -0
- package/src/lab/ledger/artifact-refs.ts +127 -0
- package/src/lab/ledger/invalidation.ts +136 -0
- package/src/lab/ledger/purge.ts +310 -0
- package/src/lab/ledger/store.ts +532 -0
- package/src/lab/live/credential-lease.ts +53 -0
- package/src/lab/live/destination.ts +155 -0
- package/src/lab/live/executor.ts +336 -0
- package/src/lab/live/inert-tools.ts +56 -0
- package/src/lab/live/manifest.ts +85 -0
- package/src/lab/live/mcp-loopback.ts +57 -0
- package/src/lab/live/runner.ts +19 -0
- package/src/lab/live/sandbox.ts +61 -0
- package/src/lab/live/suite-manifest.ts +41 -0
- package/src/lab/live/transport.ts +118 -0
- package/src/lab/live/types.ts +197 -0
- package/src/lab/observe/from-conformance.ts +301 -0
- package/src/lab/observe/from-live.ts +117 -0
- package/src/lab/paths.ts +153 -0
- package/src/lab/projection/rebuild.ts +495 -0
- package/src/lab/projection/schema.ts +135 -0
- package/src/lab/projection/verdicts.ts +474 -0
- package/src/lab/projection/verification.ts +412 -0
- package/src/lab/public/bundle.ts +217 -0
- package/src/lab/public/community-authority.ts +175 -0
- package/src/lab/public/community-files.ts +29 -0
- package/src/lab/public/community.ts +479 -0
- package/src/lab/public/file-safety.ts +155 -0
- package/src/lab/public/ids.ts +26 -0
- package/src/lab/public/index.ts +16 -0
- package/src/lab/public/mutation-lock.ts +424 -0
- package/src/lab/public/operator.ts +353 -0
- package/src/lab/public/origin-purge.ts +79 -0
- package/src/lab/public/origin.ts +203 -0
- package/src/lab/public/privacy.ts +143 -0
- package/src/lab/public/private-file.ts +261 -0
- package/src/lab/public/project.ts +124 -0
- package/src/lab/public/purge-test-fault.ts +21 -0
- package/src/lab/public/purge.ts +223 -0
- package/src/lab/public/registry.ts +44 -0
- package/src/lab/public/revocation.ts +252 -0
- package/src/lab/public/signature.ts +243 -0
- package/src/lab/public/storage.ts +105 -0
- package/src/lab/public/strict-json.ts +206 -0
- package/src/lab/public/time.ts +26 -0
- package/src/lab/public/types.ts +172 -0
- package/src/lab/public/validate.ts +391 -0
- package/src/lab/query/catalog.ts +101 -0
- package/src/lab/query/connection.ts +107 -0
- package/src/lab/query/constants.ts +4 -0
- package/src/lab/query/cursor.ts +132 -0
- package/src/lab/query/dto-map.ts +277 -0
- package/src/lab/query/errors.ts +22 -0
- package/src/lab/query/freshness.ts +53 -0
- package/src/lab/query/index.ts +45 -0
- package/src/lab/query/latest-observation.ts +59 -0
- package/src/lab/query/passive-production.ts +159 -0
- package/src/lab/query/queries.ts +444 -0
- package/src/lab/query/types.ts +266 -0
- package/src/lab/subject/behavior-fingerprint.ts +77 -0
- package/src/lab/subject/installation-salt.ts +112 -0
- package/src/lab/subject/protocol-subject.ts +80 -0
- package/src/lab/subject/route-subject.ts +74 -0
- package/src/lib/admin-secrets.ts +24 -0
- package/src/lib/app-owned-memory-stores.ts +22 -0
- package/src/lib/bounded-body.ts +167 -11
- package/src/lib/bun-runtime.ts +125 -12
- package/src/lib/bun-stream-caps.ts +13 -9
- package/src/lib/codex-restart-contract.ts +120 -0
- package/src/lib/config-ownership.ts +26 -2
- package/src/lib/destination-policy.ts +65 -1
- package/src/lib/errors.ts +80 -5
- package/src/lib/fabric-task-execution-authority.ts +7 -0
- package/src/lib/fabric-task-host.ts +29 -0
- package/src/lib/lab-activation.ts +223 -0
- package/src/lib/lab-live-execution-authority.ts +13 -0
- package/src/lib/lab-live-host.ts +30 -0
- package/src/lib/lab-live-pinned-sender.ts +56 -0
- package/src/lib/lab-live-route-production.ts +130 -0
- package/src/lib/lab-passive-linker-registration.ts +26 -0
- package/src/lib/local-management-attestation.ts +51 -0
- package/src/lib/local-management-capability.ts +100 -0
- package/src/lib/local-provider-reload-contract.ts +100 -0
- package/src/lib/optional-shutdown-hooks.ts +57 -0
- package/src/lib/package-tree-integrity.ts +101 -0
- package/src/lib/pinned-http.ts +145 -26
- package/src/lib/process-control.ts +6 -2
- package/src/lib/provider-outbound.ts +49 -9
- package/src/lib/redact.ts +419 -3
- package/src/lib/self-launch-argv.ts +15 -0
- package/src/lib/server-resource-ownership.ts +71 -0
- package/src/lib/service-secrets.ts +15 -0
- package/src/lib/shadow-call.ts +35 -4
- package/src/lib/sse-decoder.ts +41 -0
- package/src/lib/state-store-registrations.ts +10 -2
- package/src/lib/system-restart-contract.ts +73 -0
- package/src/lib/token-estimate.ts +19 -2
- package/src/lib/tool-argument-integers.ts +253 -0
- package/src/lib/translator-budget.ts +44 -0
- package/src/lib/upstream-http-version.ts +57 -0
- package/src/lib/upstream-reachability.ts +95 -0
- package/src/lib/upstream-retry.ts +156 -3
- package/src/lib/windows-atomic-replace.ts +155 -0
- package/src/lib/windows-elevation.ts +70 -2
- package/src/lib/windows-secret-acl.ts +409 -69
- package/src/lib/windows-service-wrappers.ts +72 -0
- package/src/lib/windows-text.ts +106 -0
- package/src/lib/windows-user-principal.ts +341 -0
- package/src/lib/winsw.ts +33 -5
- package/src/oauth/account-import/google-antigravity-adapter.ts +74 -0
- package/src/oauth/account-import/index.ts +15 -0
- package/src/oauth/account-import/parser.ts +83 -0
- package/src/oauth/account-import/registry.ts +18 -0
- package/src/oauth/account-import/service.ts +75 -0
- package/src/oauth/account-import/types.ts +91 -0
- package/src/oauth/anthropic.ts +12 -1
- package/src/oauth/callback-server.ts +30 -4
- package/src/oauth/chatgpt.ts +12 -1
- package/src/oauth/cline.ts +203 -0
- package/src/oauth/command-code.ts +239 -0
- package/src/oauth/cursor.ts +46 -5
- package/src/oauth/generic-account-failover.ts +231 -0
- package/src/oauth/google-antigravity.ts +35 -3
- package/src/oauth/health.ts +20 -12
- package/src/oauth/index.ts +462 -71
- package/src/oauth/key-providers.ts +25 -0
- package/src/oauth/kimi.ts +25 -3
- package/src/oauth/kiro.ts +95 -6
- package/src/oauth/local-token-detect.ts +13 -2
- package/src/oauth/log.ts +3 -1
- package/src/oauth/login-cli.ts +88 -28
- package/src/oauth/nous.ts +798 -0
- package/src/oauth/open-browser-choice.ts +26 -0
- package/src/oauth/store.ts +133 -26
- package/src/oauth/token-guardian.ts +9 -3
- package/src/oauth/types.ts +15 -0
- package/src/pi/models.ts +2 -2
- package/src/providers/alibaba-region-migration.ts +1 -1
- package/src/providers/antigravity-models.ts +521 -31
- package/src/providers/auto-compact-budget.ts +65 -0
- package/src/providers/base-url-choices.ts +10 -0
- package/src/providers/codex-capacity.ts +292 -0
- package/src/providers/command-code-efforts.ts +176 -0
- package/src/providers/context-cap.ts +26 -8
- package/src/providers/cursor-pool.ts +72 -0
- package/src/providers/default-aliases.ts +65 -0
- package/src/providers/derive.ts +281 -6
- package/src/providers/fastwire.ts +507 -0
- package/src/providers/free-directory.ts +10 -7
- package/src/providers/google-vertex-location.ts +14 -0
- package/src/providers/key-failover.ts +71 -3
- package/src/providers/label.ts +35 -2
- package/src/providers/model-discovery-limits.ts +16 -0
- package/src/providers/model-discovery.ts +115 -22
- package/src/providers/model-presets.ts +119 -0
- package/src/providers/model-rename-migration.ts +255 -0
- package/src/providers/model-rename-startup.ts +28 -0
- package/src/providers/new-model-policy.ts +146 -0
- package/src/providers/openai-sidecar.ts +72 -4
- package/src/providers/openai-tier-startup.ts +31 -2
- package/src/providers/openai-tiers.ts +119 -4
- package/src/providers/openai-virtual-models.ts +1 -0
- package/src/providers/opencode-zen-rate-limit.ts +102 -0
- package/src/providers/provider-id-rewrite.ts +30 -0
- package/src/providers/quota.ts +1379 -40
- package/src/providers/registry.ts +1545 -112
- package/src/providers/request-pacing.ts +310 -0
- package/src/providers/service-tier.ts +277 -0
- package/src/providers/slug-codec.ts +94 -6
- package/src/providers/static-model-discovery.ts +86 -0
- package/src/providers/xai-responses-opt-in.ts +15 -0
- package/src/providers/xai-transport.ts +32 -4
- package/src/reasoning-effort.ts +68 -3
- package/src/responses/apply-patch-envelope.ts +63 -0
- package/src/responses/code-mode-helper-compat.ts +50 -0
- package/src/responses/compaction.ts +26 -1
- package/src/responses/custom-tool-compat.ts +384 -0
- package/src/responses/hosted-tool-policy.ts +9 -0
- package/src/responses/namespace-tool-compat.ts +435 -0
- package/src/responses/parser.ts +225 -38
- package/src/responses/provider-continuation.ts +98 -0
- package/src/responses/provider-opaque-metadata.ts +73 -0
- package/src/responses/reasoning-envelope.ts +9 -1
- package/src/responses/reasoning-replay-cache.ts +504 -0
- package/src/responses/schema.ts +12 -2
- package/src/responses/spill-store.ts +75 -10
- package/src/responses/state.ts +659 -31
- package/src/responses/thought-signature-replay.ts +364 -0
- package/src/responses/tool-search-compat.ts +301 -0
- package/src/responses/truncated-stop-reason.ts +60 -0
- package/src/responses/turn-termination.ts +107 -0
- package/src/router.ts +405 -31
- package/src/routing/analytics.ts +379 -0
- package/src/routing/capability.ts +244 -0
- package/src/routing/compatibility/assemble.ts +73 -0
- package/src/routing/compatibility/behavior.ts +278 -0
- package/src/routing/compatibility/catalog.ts +99 -0
- package/src/routing/compatibility/endpoint.ts +52 -0
- package/src/routing/compatibility/lab-evidence-provider.ts +130 -0
- package/src/routing/compatibility/policy.ts +181 -0
- package/src/routing/compatibility/provider-slot.ts +56 -0
- package/src/routing/compatibility/reader.ts +110 -0
- package/src/routing/compatibility/subject.ts +191 -0
- package/src/routing/compatibility/types.ts +64 -0
- package/src/routing/compatibility/version.ts +104 -0
- package/src/routing/cost.ts +77 -0
- package/src/routing/evaluator.ts +495 -0
- package/src/routing/health.ts +412 -0
- package/src/routing/history/cursor.ts +43 -0
- package/src/routing/history/indexer.ts +605 -0
- package/src/routing/history/schema.ts +72 -0
- package/src/routing/profile-namespace.ts +15 -0
- package/src/routing/profile.ts +547 -0
- package/src/routing/quota.ts +155 -0
- package/src/routing/request-evidence.ts +45 -0
- package/src/routing/trace.ts +776 -0
- package/src/server/adapter-resolve.ts +2 -29
- package/src/server/auth-cors.ts +289 -48
- package/src/server/background-lifecycle.ts +182 -0
- package/src/server/chat-completions.ts +154 -72
- package/src/server/chat-native-sse.ts +331 -0
- package/src/server/chat-native.ts +445 -0
- package/src/server/claude-messages.ts +160 -44
- package/src/server/direct-local-http.ts +347 -0
- package/src/server/effort-policy.ts +18 -0
- package/src/server/github-copilot-responses-repair.ts +338 -0
- package/src/server/gui-static.ts +39 -10
- package/src/server/images.ts +94 -12
- package/src/server/index.ts +1021 -190
- package/src/server/lifecycle.ts +336 -14
- package/src/server/live.ts +136 -17
- package/src/server/local-management-read-client.ts +90 -0
- package/src/server/local-provider-reload-client.ts +137 -0
- package/src/server/management/agent-settings-routes.ts +433 -116
- package/src/server/management/api-key-usage.ts +31 -5
- package/src/server/management/body.ts +6 -0
- package/src/server/management/codex-prompt-routes.ts +570 -0
- package/src/server/management/combo-routes.ts +64 -25
- package/src/server/management/config-routes.ts +511 -51
- package/src/server/management/context.ts +89 -2
- package/src/server/management/integration-routes.ts +498 -0
- package/src/server/management/lab-automation-routes.ts +206 -0
- package/src/server/management/lab-routes.ts +563 -0
- package/src/server/management/logs-usage-routes.ts +127 -52
- package/src/server/management/model-routes.ts +461 -133
- package/src/server/management/model-rows.ts +167 -0
- package/src/server/management/native-integration-routes.ts +770 -0
- package/src/server/management/oauth-account-routes.ts +90 -8
- package/src/server/management/provider-capability-config.ts +48 -0
- package/src/server/management/provider-routes.ts +836 -159
- package/src/server/management/request-history-routes.ts +191 -0
- package/src/server/management/route-registry.ts +332 -0
- package/src/server/management/routing-analytics-routes.ts +74 -0
- package/src/server/management/routing-profile-routes.ts +380 -0
- package/src/server/management/shared.ts +27 -11
- package/src/server/management/sidebar-routes.ts +47 -31
- package/src/server/management/storage-log-guard-routes.ts +186 -0
- package/src/server/management/sync-response.ts +69 -0
- package/src/server/management/system-restart.ts +289 -33
- package/src/server/management/system-routes.ts +77 -0
- package/src/server/management/usage-summary-cache.ts +9 -1
- package/src/server/management/vision-sidecar-options.ts +167 -0
- package/src/server/management/web-search-sidecar-options.ts +120 -0
- package/src/server/management-api.ts +126 -23
- package/src/server/management-auth.ts +220 -5
- package/src/server/passive-route-linker.ts +66 -0
- package/src/server/port-reclaim.ts +1 -1
- package/src/server/ports.ts +41 -1
- package/src/server/proxy-liveness.ts +161 -10
- package/src/server/readiness.ts +99 -0
- package/src/server/relay-eager.ts +221 -54
- package/src/server/relay.ts +479 -87
- package/src/server/request-decompress.ts +113 -6
- package/src/server/request-log-conversation.ts +51 -0
- package/src/server/request-log.ts +298 -31
- package/src/server/responses/agent-task-recovery-cache.ts +143 -0
- package/src/server/responses/agent-task-recovery.ts +465 -0
- package/src/server/responses/codex-auth-error.ts +55 -0
- package/src/server/responses/collaboration.ts +239 -36
- package/src/server/responses/combo-stream-preflight.ts +178 -0
- package/src/server/responses/compact.ts +444 -72
- package/src/server/responses/core.ts +3579 -436
- package/src/server/responses/empty-completion-guard.ts +333 -0
- package/src/server/responses/encrypted-payload.ts +62 -39
- package/src/server/responses/fetch-helpers.ts +89 -99
- package/src/server/responses/input-admission.ts +187 -0
- package/src/server/responses/pacing-overload.ts +13 -0
- package/src/server/responses/passthrough-error.ts +33 -9
- package/src/server/responses/policy-fallback.ts +178 -0
- package/src/server/responses/responses-field-backfill.ts +343 -0
- package/src/server/responses/terminal-guard.ts +26 -5
- package/src/server/responses/upstream-error.ts +5 -0
- package/src/server/responses/ws-upstream.ts +381 -0
- package/src/server/responses-custom-tool-repair.ts +363 -0
- package/src/server/responses-item-id-repair.ts +54 -6
- package/src/server/responses-json-events.ts +90 -0
- package/src/server/responses-model-rewrite.ts +29 -0
- package/src/server/responses-reasoning-summary-rewrite.ts +178 -0
- package/src/server/responses-snapshot-repair.ts +621 -0
- package/src/server/responses-terminal-repair.ts +363 -0
- package/src/server/responses-tool-search-repair.ts +267 -0
- package/src/server/responses-undeclared-tool-guard.ts +376 -0
- package/src/server/responses.ts +18 -2
- package/src/server/search.ts +78 -13
- package/src/server/sse-frame-buffer.ts +319 -0
- package/src/server/sse-payload-rewrite.ts +110 -22
- package/src/server/startup-action-control.ts +8 -1
- package/src/server/startup-health-cache.ts +19 -1
- package/src/server/system-env.ts +80 -9
- package/src/server/ws-bridge.ts +53 -40
- package/src/service-manager-probe.ts +892 -0
- package/src/service.ts +1153 -94
- package/src/sidecar/auth.ts +92 -0
- package/src/sidecar/candidates.ts +83 -0
- package/src/storage/cleanup.ts +2 -2
- package/src/storage/policy-job.ts +14 -4
- package/src/storage/policy.ts +88 -23
- package/src/storage/scanner.ts +1 -1
- package/src/storage/worker-lifecycle.ts +14 -14
- package/src/tray/windows-tray.ps1 +83 -9
- package/src/tray/windows.ts +43 -16
- package/src/types/accounts.ts +37 -0
- package/src/types/config.ts +911 -0
- package/src/types/provider.ts +614 -0
- package/src/types/request.ts +397 -0
- package/src/types/tools.ts +236 -0
- package/src/types/wire.ts +80 -0
- package/src/types.ts +107 -1237
- package/src/update/index.ts +37 -23
- package/src/update/job.ts +445 -68
- package/src/update/notify.ts +12 -6
- package/src/update/npm-cache-preflight.d.mts +47 -0
- package/src/update/npm-cache-preflight.mjs +201 -0
- package/src/update/transactional-install.d.mts +22 -0
- package/src/update/transactional-install.mjs +266 -0
- package/src/usage/cost.ts +0 -0
- package/src/usage/expected-prices.ts +287 -16
- package/src/usage/log.ts +666 -50
- package/src/usage/summary.ts +388 -17
- package/src/usage/user-cost-overlay-reconciler.ts +313 -0
- package/src/usage/user-cost-overlays.ts +314 -0
- package/src/vision/anthropic-describe.ts +10 -6
- package/src/vision/backends.ts +97 -0
- package/src/vision/describe.ts +27 -16
- package/src/vision/eligibility.ts +250 -0
- package/src/vision/index.ts +238 -24
- package/src/vision/reasoning.ts +55 -0
- package/src/vision/routed-describe.ts +175 -0
- package/src/vision/timeout-bounds.ts +9 -0
- package/src/web-search/anthropic-executor.ts +13 -7
- package/src/web-search/backends.ts +108 -0
- package/src/web-search/exa-executor.ts +119 -0
- package/src/web-search/executor.ts +21 -6
- package/src/web-search/gemini-executor.ts +141 -0
- package/src/web-search/index.ts +150 -15
- package/src/web-search/loop.ts +284 -54
- package/src/web-search/parse.ts +125 -30
- package/src/web-search/sources.ts +60 -0
- package/src/web-search/xai-executor.ts +219 -0
- package/gui/dist/assets/index-DTpMHS4F.js +0 -67
- package/gui/dist/assets/index-ZNVDE3C7.css +0 -1
- package/gui/dist/provider-icons/antigravity.svg +0 -1
- package/gui/dist/provider-icons/claude.svg +0 -1
- package/gui/dist/provider-icons/copilot.svg +0 -1
- package/gui/dist/provider-icons/cursor.svg +0 -2
- package/gui/dist/provider-icons/gemini.svg +0 -1
- package/gui/dist/provider-icons/grok-color.svg +0 -1
- package/gui/dist/provider-icons/kiro.svg +0 -14
- package/src/cli/internal-dispatch.ts +0 -20
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client-facing repair for GitHub Copilot's `vscode-chat` Responses stream.
|
|
3
|
+
*
|
|
4
|
+
* Copilot can rotate opaque response/item ids between lifecycle events, expose
|
|
5
|
+
* provider-private reasoning ciphertext, and attach per-delta obfuscation
|
|
6
|
+
* padding while the authoritative complete tool input arrives on `.done`.
|
|
7
|
+
* Stock Responses clients cannot reconcile that dialect. This stateful block
|
|
8
|
+
* rewrite emits one coherent Responses SSE stream while the inspection branch
|
|
9
|
+
* keeps the raw upstream snapshot for provider replay.
|
|
10
|
+
*/
|
|
11
|
+
import {
|
|
12
|
+
TranslatorBudgetExceededError,
|
|
13
|
+
type TranslatorBudget,
|
|
14
|
+
} from "../lib/translator-budget";
|
|
15
|
+
import {
|
|
16
|
+
replaceSseDataPayload,
|
|
17
|
+
sseDataPayload,
|
|
18
|
+
type SseBlockRewrite,
|
|
19
|
+
} from "./sse-payload-rewrite";
|
|
20
|
+
|
|
21
|
+
const MAX_TRACKED_ITEMS = 256;
|
|
22
|
+
const MAX_TRACKED_DEFERRED_CALLS = 256;
|
|
23
|
+
const RETAINED_STATE_LIMIT_BYTES = 256 * 1024;
|
|
24
|
+
|
|
25
|
+
type JsonRecord = Record<string, unknown>;
|
|
26
|
+
|
|
27
|
+
type TrackedItem = {
|
|
28
|
+
id: string;
|
|
29
|
+
type?: string;
|
|
30
|
+
encryptedReasoning: boolean;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
type DeferredCallKind = "function" | "custom";
|
|
34
|
+
|
|
35
|
+
function isRecord(value: unknown): value is JsonRecord {
|
|
36
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function outputIndexOf(value: unknown): number | undefined {
|
|
40
|
+
return typeof value === "number" && Number.isInteger(value) && value >= 0
|
|
41
|
+
? value
|
|
42
|
+
: undefined;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function byteLength(value: unknown): number {
|
|
46
|
+
return Buffer.byteLength(JSON.stringify(value), "utf8");
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function blockNewline(block: string): "\r\n" | "\n" {
|
|
50
|
+
return block.includes("\r\n") ? "\r\n" : "\n";
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Keep a named SSE block's `event:` field aligned with its JSON `type`. */
|
|
54
|
+
function withEventName(block: string, eventName: string): string {
|
|
55
|
+
const newline = blockNewline(block);
|
|
56
|
+
const lines = block.split(/\r?\n/);
|
|
57
|
+
let replaced = false;
|
|
58
|
+
const rewritten = lines.flatMap((line) => {
|
|
59
|
+
if (!line.startsWith("event:")) return [line];
|
|
60
|
+
if (replaced) return [];
|
|
61
|
+
replaced = true;
|
|
62
|
+
return [`event: ${eventName}`];
|
|
63
|
+
});
|
|
64
|
+
if (!replaced) {
|
|
65
|
+
const dataIndex = rewritten.findIndex(line => line.startsWith("data:"));
|
|
66
|
+
rewritten.splice(dataIndex >= 0 ? dataIndex : 0, 0, `event: ${eventName}`);
|
|
67
|
+
}
|
|
68
|
+
return rewritten.join(newline);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function rewriteJsonBlock(block: string, event: JsonRecord): string {
|
|
72
|
+
const type = typeof event.type === "string" ? event.type : "message";
|
|
73
|
+
return withEventName(replaceSseDataPayload(block, JSON.stringify(event)), type);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function syntheticJsonBlock(template: string, event: JsonRecord): string {
|
|
77
|
+
const newline = blockNewline(template);
|
|
78
|
+
const type = typeof event.type === "string" ? event.type : "message";
|
|
79
|
+
return `event: ${type}${newline}data: ${JSON.stringify(event)}`;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function callKindForDelta(type: unknown): DeferredCallKind | undefined {
|
|
83
|
+
if (type === "response.function_call_arguments.delta") return "function";
|
|
84
|
+
if (type === "response.custom_tool_call_input.delta") return "custom";
|
|
85
|
+
return undefined;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function callKindForDone(type: unknown): DeferredCallKind | undefined {
|
|
89
|
+
if (type === "response.function_call_arguments.done") return "function";
|
|
90
|
+
if (type === "response.custom_tool_call_input.done") return "custom";
|
|
91
|
+
return undefined;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function callKey(kind: DeferredCallKind, outputIndex: number): string {
|
|
95
|
+
return `${kind}:${outputIndex}`;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function deltaTypeFor(kind: DeferredCallKind): string {
|
|
99
|
+
return kind === "function"
|
|
100
|
+
? "response.function_call_arguments.delta"
|
|
101
|
+
: "response.custom_tool_call_input.delta";
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function completeValueFor(kind: DeferredCallKind, event: JsonRecord): string {
|
|
105
|
+
const value = kind === "function" ? event.arguments : event.input;
|
|
106
|
+
return typeof value === "string" ? value : "";
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Create one provider-scoped block rewrite per upstream response.
|
|
111
|
+
*
|
|
112
|
+
* Retained ids and pending-call markers are charged to the request translator
|
|
113
|
+
* budget and hard-capped. `dispose` releases them on terminal, EOF, cancel, or
|
|
114
|
+
* relay failure through the shared block-rewrite lifecycle.
|
|
115
|
+
*/
|
|
116
|
+
export function createGithubCopilotResponsesBlockRewrite(
|
|
117
|
+
budget?: TranslatorBudget,
|
|
118
|
+
): SseBlockRewrite {
|
|
119
|
+
const items = new Map<number, TrackedItem>();
|
|
120
|
+
const deferredCalls = new Map<string, number>();
|
|
121
|
+
let responseId: string | undefined;
|
|
122
|
+
let itemStateBytes = 0;
|
|
123
|
+
let callStateBytes = 0;
|
|
124
|
+
let nextSequenceNumber = 0;
|
|
125
|
+
let itemTrackingTainted = false;
|
|
126
|
+
let disposed = false;
|
|
127
|
+
|
|
128
|
+
const ensureStateLimit = (nextBytes: number): void => {
|
|
129
|
+
if (itemStateBytes + callStateBytes + nextBytes > RETAINED_STATE_LIMIT_BYTES) {
|
|
130
|
+
throw new TranslatorBudgetExceededError("item_ids", RETAINED_STATE_LIMIT_BYTES);
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
const chargeItemState = (bytes: number): void => {
|
|
135
|
+
ensureStateLimit(bytes);
|
|
136
|
+
budget?.chargeRetained(bytes, { kind: "item_ids" });
|
|
137
|
+
itemStateBytes += bytes;
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
const chargeCallState = (bytes: number): void => {
|
|
141
|
+
ensureStateLimit(bytes);
|
|
142
|
+
budget?.chargeRetained(bytes, { kind: "retained_collectors" });
|
|
143
|
+
callStateBytes += bytes;
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
const releaseDeferredCall = (key: string): boolean => {
|
|
147
|
+
const bytes = deferredCalls.get(key);
|
|
148
|
+
if (bytes === undefined) return false;
|
|
149
|
+
deferredCalls.delete(key);
|
|
150
|
+
callStateBytes -= bytes;
|
|
151
|
+
budget?.releaseRetained(bytes, { kind: "retained_collectors" });
|
|
152
|
+
return true;
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
const rememberResponseId = (candidate: string): string => {
|
|
156
|
+
if (responseId !== undefined) return responseId;
|
|
157
|
+
const bytes = byteLength(candidate);
|
|
158
|
+
chargeItemState(bytes);
|
|
159
|
+
responseId = candidate;
|
|
160
|
+
return candidate;
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
const rememberItem = (
|
|
164
|
+
outputIndex: number,
|
|
165
|
+
candidateId: string,
|
|
166
|
+
itemType?: string,
|
|
167
|
+
encryptedReasoning = false,
|
|
168
|
+
): TrackedItem => {
|
|
169
|
+
const existing = items.get(outputIndex);
|
|
170
|
+
if (existing) {
|
|
171
|
+
if (existing.type === undefined && itemType !== undefined) {
|
|
172
|
+
const extra = byteLength(itemType);
|
|
173
|
+
chargeItemState(extra);
|
|
174
|
+
existing.type = itemType;
|
|
175
|
+
}
|
|
176
|
+
existing.encryptedReasoning ||= encryptedReasoning;
|
|
177
|
+
return existing;
|
|
178
|
+
}
|
|
179
|
+
if (itemTrackingTainted || items.size >= MAX_TRACKED_ITEMS) {
|
|
180
|
+
// Keep relaying client-visible events after the count cap, but stop
|
|
181
|
+
// retaining new output-index mappings. The current event still receives
|
|
182
|
+
// the provider id, while earlier retained mappings remain available for
|
|
183
|
+
// stable ids. Byte-budget failures stay hard failures.
|
|
184
|
+
itemTrackingTainted = true;
|
|
185
|
+
return { id: candidateId, type: itemType, encryptedReasoning };
|
|
186
|
+
}
|
|
187
|
+
chargeItemState(byteLength([outputIndex, candidateId, itemType ?? null]));
|
|
188
|
+
const tracked = { id: candidateId, type: itemType, encryptedReasoning };
|
|
189
|
+
items.set(outputIndex, tracked);
|
|
190
|
+
return tracked;
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
const rememberDeferredCall = (key: string): void => {
|
|
194
|
+
if (deferredCalls.has(key)) return;
|
|
195
|
+
if (deferredCalls.size >= MAX_TRACKED_DEFERRED_CALLS) {
|
|
196
|
+
throw new TranslatorBudgetExceededError("retained_collectors", RETAINED_STATE_LIMIT_BYTES);
|
|
197
|
+
}
|
|
198
|
+
const bytes = byteLength(key);
|
|
199
|
+
chargeCallState(bytes);
|
|
200
|
+
deferredCalls.set(key, bytes);
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
const sanitizeReasoningItem = (
|
|
204
|
+
item: JsonRecord,
|
|
205
|
+
tracked: TrackedItem,
|
|
206
|
+
): void => {
|
|
207
|
+
if (item.type !== "reasoning") return;
|
|
208
|
+
const encrypted = typeof item.encrypted_content === "string";
|
|
209
|
+
tracked.encryptedReasoning ||= encrypted;
|
|
210
|
+
if (encrypted) delete item.encrypted_content;
|
|
211
|
+
if (tracked.encryptedReasoning) {
|
|
212
|
+
const summary = Array.isArray(item.summary) ? item.summary : [];
|
|
213
|
+
const hasPlaintextSummary = summary.some(part =>
|
|
214
|
+
isRecord(part)
|
|
215
|
+
&& part.type === "summary_text"
|
|
216
|
+
&& typeof part.text === "string"
|
|
217
|
+
&& part.text.length > 0);
|
|
218
|
+
if (!hasPlaintextSummary) item.summary = [{ type: "summary_text", text: "" }];
|
|
219
|
+
if (!Array.isArray(item.content)) item.content = [];
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
const normalizeNestedItem = (item: JsonRecord, outputIndex: number): void => {
|
|
224
|
+
const candidateId = typeof item.id === "string" ? item.id : undefined;
|
|
225
|
+
if (candidateId === undefined) return;
|
|
226
|
+
const itemType = typeof item.type === "string" ? item.type : undefined;
|
|
227
|
+
const tracked = rememberItem(
|
|
228
|
+
outputIndex,
|
|
229
|
+
candidateId,
|
|
230
|
+
itemType,
|
|
231
|
+
itemType === "reasoning" && typeof item.encrypted_content === "string",
|
|
232
|
+
);
|
|
233
|
+
item.id = tracked.id;
|
|
234
|
+
sanitizeReasoningItem(item, tracked);
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
const normalizeSequence = (event: JsonRecord): void => {
|
|
238
|
+
if (typeof event.sequence_number !== "number" || !Number.isFinite(event.sequence_number)) return;
|
|
239
|
+
event.sequence_number = nextSequenceNumber++;
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
const dispose = (): void => {
|
|
243
|
+
if (disposed) return;
|
|
244
|
+
disposed = true;
|
|
245
|
+
if (itemStateBytes > 0) budget?.releaseRetained(itemStateBytes, { kind: "item_ids" });
|
|
246
|
+
if (callStateBytes > 0) budget?.releaseRetained(callStateBytes, { kind: "retained_collectors" });
|
|
247
|
+
items.clear();
|
|
248
|
+
deferredCalls.clear();
|
|
249
|
+
responseId = undefined;
|
|
250
|
+
itemStateBytes = 0;
|
|
251
|
+
callStateBytes = 0;
|
|
252
|
+
itemTrackingTainted = false;
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
const rewrite: SseBlockRewrite = (block) => {
|
|
256
|
+
const payload = sseDataPayload(block);
|
|
257
|
+
if (payload === null || payload === "[DONE]") return [block];
|
|
258
|
+
|
|
259
|
+
let event: unknown;
|
|
260
|
+
try {
|
|
261
|
+
event = JSON.parse(payload);
|
|
262
|
+
} catch {
|
|
263
|
+
return [block];
|
|
264
|
+
}
|
|
265
|
+
if (!isRecord(event)) return [block];
|
|
266
|
+
|
|
267
|
+
if (typeof event.obfuscation === "string") delete event.obfuscation;
|
|
268
|
+
|
|
269
|
+
const type = event.type;
|
|
270
|
+
const outputIndex = outputIndexOf(event.output_index);
|
|
271
|
+
const deltaKind = callKindForDelta(type);
|
|
272
|
+
const deferToolDelta = deltaKind !== undefined
|
|
273
|
+
&& outputIndex !== undefined;
|
|
274
|
+
|
|
275
|
+
if (isRecord(event.response) && typeof event.response.id === "string") {
|
|
276
|
+
event.response.id = rememberResponseId(event.response.id);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
if (outputIndex !== undefined && isRecord(event.item)) {
|
|
280
|
+
normalizeNestedItem(event.item, outputIndex);
|
|
281
|
+
}
|
|
282
|
+
// A deferred delta is not client-visible, so it must not establish the
|
|
283
|
+
// public item id if a malformed provider sends it before output_item.added.
|
|
284
|
+
if (!deferToolDelta && outputIndex !== undefined && typeof event.item_id === "string") {
|
|
285
|
+
const tracked = rememberItem(outputIndex, event.item_id);
|
|
286
|
+
event.item_id = tracked.id;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
if (type === "response.completed" && isRecord(event.response) && Array.isArray(event.response.output)) {
|
|
290
|
+
event.response.output.forEach((item, index) => {
|
|
291
|
+
if (isRecord(item)) normalizeNestedItem(item, index);
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if (typeof type === "string" && type.startsWith("response.reasoning")) {
|
|
296
|
+
if (typeof event.encrypted_content === "string") delete event.encrypted_content;
|
|
297
|
+
// `encrypted_content` is opaque replay state; summary delta/text fields
|
|
298
|
+
// are a separate, potentially readable part of the public Responses
|
|
299
|
+
// contract and must survive when Copilot provides them.
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
if (deferToolDelta) {
|
|
303
|
+
rememberDeferredCall(callKey(deltaKind, outputIndex));
|
|
304
|
+
// The OpenAI `obfuscation` field is padding, not a decryption key. Defer
|
|
305
|
+
// every Copilot tool fragment anyway: mixed padded/unpadded fragments and
|
|
306
|
+
// rotating ids cannot then produce duplicate or irreconcilable input.
|
|
307
|
+
// `.done` carries the authoritative complete value, which is emitted as
|
|
308
|
+
// one canonical delta immediately before the original `.done` event.
|
|
309
|
+
return [];
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
const emitted: Array<{ event: JsonRecord; synthetic: boolean }> = [];
|
|
313
|
+
const doneKind = callKindForDone(type);
|
|
314
|
+
if (doneKind !== undefined && outputIndex !== undefined) {
|
|
315
|
+
const key = callKey(doneKind, outputIndex);
|
|
316
|
+
if (releaseDeferredCall(key)) {
|
|
317
|
+
emitted.push({
|
|
318
|
+
synthetic: true,
|
|
319
|
+
event: {
|
|
320
|
+
type: deltaTypeFor(doneKind),
|
|
321
|
+
output_index: outputIndex,
|
|
322
|
+
...(typeof event.item_id === "string" ? { item_id: event.item_id } : {}),
|
|
323
|
+
...(typeof event.sequence_number === "number" ? { sequence_number: event.sequence_number } : {}),
|
|
324
|
+
delta: completeValueFor(doneKind, event),
|
|
325
|
+
},
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
emitted.push({ event, synthetic: false });
|
|
330
|
+
|
|
331
|
+
for (const entry of emitted) normalizeSequence(entry.event);
|
|
332
|
+
return emitted.map(entry => entry.synthetic
|
|
333
|
+
? syntheticJsonBlock(block, entry.event)
|
|
334
|
+
: rewriteJsonBlock(block, entry.event));
|
|
335
|
+
};
|
|
336
|
+
rewrite.dispose = dispose;
|
|
337
|
+
return rewrite;
|
|
338
|
+
}
|
package/src/server/gui-static.ts
CHANGED
|
@@ -56,16 +56,25 @@ function isFile(path: string): boolean {
|
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
59
|
+
/** HTML-attribute escape for values interpolated into meta tags. */
|
|
60
|
+
function escapeHtmlAttribute(value: string): string {
|
|
61
|
+
return value
|
|
62
|
+
.replaceAll("&", "&")
|
|
63
|
+
.replaceAll('"', """)
|
|
64
|
+
.replaceAll("<", "<")
|
|
65
|
+
.replaceAll(">", ">");
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Shared session meta-tag block, escaped for quoted attribute interpolation. */
|
|
69
|
+
function sessionBootstrapMeta(session: GuiSessionBootstrap): string {
|
|
70
|
+
return [
|
|
71
|
+
`<meta name="opencodex-session-token" content="${escapeHtmlAttribute(session.token)}">`,
|
|
72
|
+
`<meta name="opencodex-session-csrf" content="${escapeHtmlAttribute(session.csrfToken)}">`,
|
|
73
|
+
`<meta name="opencodex-session-origin" content="${escapeHtmlAttribute(session.origin)}">`,
|
|
74
|
+
].join("");
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function htmlDocumentResponse(html: string): Response {
|
|
69
78
|
return new Response(html, {
|
|
70
79
|
headers: {
|
|
71
80
|
"Content-Type": "text/html",
|
|
@@ -76,6 +85,26 @@ function htmlResponse(path: string, session?: GuiSessionBootstrap): Response {
|
|
|
76
85
|
});
|
|
77
86
|
}
|
|
78
87
|
|
|
88
|
+
function htmlResponse(path: string, session?: GuiSessionBootstrap): Response {
|
|
89
|
+
let html = readFileSync(path, "utf8");
|
|
90
|
+
if (session) {
|
|
91
|
+
const bootstrap = sessionBootstrapMeta(session);
|
|
92
|
+
html = html.includes("</head>") ? html.replace("</head>", `${bootstrap}</head>`) : `${bootstrap}${html}`;
|
|
93
|
+
}
|
|
94
|
+
return htmlDocumentResponse(html);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Minimal session-bootstrap document, independent of any packaged GUI build. The dev
|
|
99
|
+
* GUI (Vite) proxies /opencodex-session to the backend with the original host so the
|
|
100
|
+
* backend can mint an origin-bound loopback session even when gui/dist does not exist.
|
|
101
|
+
*/
|
|
102
|
+
export function serveSessionBootstrap(session: GuiSessionBootstrap): Response {
|
|
103
|
+
const bootstrap = sessionBootstrapMeta(session);
|
|
104
|
+
const html = `<!doctype html><html><head><meta charset="utf-8">${bootstrap}</head><body></body></html>`;
|
|
105
|
+
return htmlDocumentResponse(html);
|
|
106
|
+
}
|
|
107
|
+
|
|
79
108
|
export function serveGuiFile(
|
|
80
109
|
pathname: string,
|
|
81
110
|
guiDist = findGuiDist(),
|
package/src/server/images.ts
CHANGED
|
@@ -15,13 +15,16 @@
|
|
|
15
15
|
import { formatErrorResponse } from "../bridge";
|
|
16
16
|
import {
|
|
17
17
|
CodexAccountCooldownError,
|
|
18
|
+
codexMainProfileDrainingResponse,
|
|
18
19
|
cooldownErrorResponse,
|
|
19
20
|
CodexAuthContextError,
|
|
21
|
+
CodexMainProfileDrainingError,
|
|
20
22
|
CodexPoolAuthenticationError,
|
|
21
23
|
CodexThreadAffinityExpiredError,
|
|
22
24
|
} from "../codex/auth-context";
|
|
23
25
|
import { formatCodexProviderForLog } from "../codex/routing";
|
|
24
26
|
import { signalWithTimeout } from "../lib/abort";
|
|
27
|
+
import { readBoundedResponseBytes, type BoundedBytesResult } from "../lib/bounded-body";
|
|
25
28
|
import { sidecarEnter } from "../lib/sidecar-tracker";
|
|
26
29
|
import type { OcxConfig } from "../types";
|
|
27
30
|
import { resolveFirstUsableOpenAiSidecar, selectImagesProvider } from "../providers/openai-sidecar";
|
|
@@ -35,6 +38,8 @@ import { safeAntigravityHttpErrorMessage } from "../adapters/google-errors";
|
|
|
35
38
|
import { sanitizeUpstreamErrorText } from "../adapters/upstream-http-error";
|
|
36
39
|
import { ANTIGRAVITY_REQUEST_UA } from "../adapters/google-antigravity-wire";
|
|
37
40
|
import { decodeValidatedImageBase64, MAX_ENCODED_BYTES_PER_IMAGE } from "../images/artifacts";
|
|
41
|
+
import type { AdmissionLease } from "../lib/admission";
|
|
42
|
+
import { codexAccountSelectionForTurn } from "./lifecycle";
|
|
38
43
|
|
|
39
44
|
export type ImagesEndpoint = "generations" | "edits";
|
|
40
45
|
|
|
@@ -46,7 +51,56 @@ const IMAGES_UPSTREAM_TIMEOUT_MS = 300_000;
|
|
|
46
51
|
* containing base64-encoded images — typically a few MB. This prevents an oversized or malicious
|
|
47
52
|
* response from exhausting process memory.
|
|
48
53
|
*/
|
|
49
|
-
const IMAGES_RESPONSE_MAX_BYTES = 100 * 1024 * 1024;
|
|
54
|
+
export const IMAGES_RESPONSE_MAX_BYTES = 100 * 1024 * 1024;
|
|
55
|
+
|
|
56
|
+
interface ImageResponseBytesOptions {
|
|
57
|
+
signal?: AbortSignal;
|
|
58
|
+
/** Smaller values are used only by focused reader tests. */
|
|
59
|
+
maxBytes?: number;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function cancelUnlockedResponseBody(response: Response | undefined, reason?: unknown): void {
|
|
63
|
+
const body = response?.body;
|
|
64
|
+
if (!body || body.locked) return;
|
|
65
|
+
try {
|
|
66
|
+
void body.cancel(reason).catch(() => undefined);
|
|
67
|
+
} catch {
|
|
68
|
+
// A broken stream can throw synchronously from cancel().
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Read one image relay response as exact raw bytes under the same cap used by
|
|
74
|
+
* the public handler. A trustworthy Content-Length can reject obvious excess
|
|
75
|
+
* before attaching a reader; the streaming reader remains authoritative when
|
|
76
|
+
* the header is absent or understated.
|
|
77
|
+
*/
|
|
78
|
+
export async function readImageResponseBytes(
|
|
79
|
+
response: Response,
|
|
80
|
+
options: ImageResponseBytesOptions = {},
|
|
81
|
+
): Promise<BoundedBytesResult> {
|
|
82
|
+
const signal = options.signal;
|
|
83
|
+
if (signal?.aborted) {
|
|
84
|
+
cancelUnlockedResponseBody(response, signal.reason);
|
|
85
|
+
throw signal.reason;
|
|
86
|
+
}
|
|
87
|
+
if (response.body === null) {
|
|
88
|
+
return { bytes: new Uint8Array(0), oversized: false };
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const maxBytes = options.maxBytes ?? IMAGES_RESPONSE_MAX_BYTES;
|
|
92
|
+
const contentLength = response.headers.get("content-length");
|
|
93
|
+
const declaredBytes = contentLength === null ? Number.NaN : Number(contentLength);
|
|
94
|
+
if (Number.isSafeInteger(declaredBytes) && declaredBytes > maxBytes) {
|
|
95
|
+
cancelUnlockedResponseBody(
|
|
96
|
+
response,
|
|
97
|
+
new DOMException("Response body size limit reached", "QuotaExceededError"),
|
|
98
|
+
);
|
|
99
|
+
return { bytes: new Uint8Array(0), oversized: true };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return readBoundedResponseBytes(response, { maxBytes, signal });
|
|
103
|
+
}
|
|
50
104
|
|
|
51
105
|
const CCA_IMAGE_MODEL = "gemini-3.1-flash-image";
|
|
52
106
|
|
|
@@ -323,6 +377,7 @@ export async function handleImages(
|
|
|
323
377
|
config: OcxConfig,
|
|
324
378
|
endpoint: ImagesEndpoint,
|
|
325
379
|
logCtx: RequestLogContext,
|
|
380
|
+
turnAdmissionLease?: AdmissionLease,
|
|
326
381
|
): Promise<Response> {
|
|
327
382
|
const candidates = selectImagesProvider(config);
|
|
328
383
|
if (candidates.error) {
|
|
@@ -375,11 +430,15 @@ export async function handleImages(
|
|
|
375
430
|
let forwardAuthError: Response | undefined;
|
|
376
431
|
if (canUseOpenAiForward) {
|
|
377
432
|
try {
|
|
378
|
-
forward = await resolveFirstUsableOpenAiSidecar(candidates.forwardCandidates, req.headers, config
|
|
433
|
+
forward = await resolveFirstUsableOpenAiSidecar(candidates.forwardCandidates, req.headers, config, {
|
|
434
|
+
beginCodexAccountSelection: codexAccountSelectionForTurn(turnAdmissionLease),
|
|
435
|
+
});
|
|
379
436
|
if (forward) logCtx.provider = formatCodexProviderForLog(forward.providerName, codexLogAccountId(forward.authContext), config);
|
|
380
437
|
} catch (err) {
|
|
381
438
|
if (err instanceof CodexAccountCooldownError) {
|
|
382
439
|
forwardAuthError = cooldownErrorResponse(err);
|
|
440
|
+
} else if (err instanceof CodexMainProfileDrainingError) {
|
|
441
|
+
forwardAuthError = codexMainProfileDrainingResponse();
|
|
383
442
|
} else if (err instanceof CodexThreadAffinityExpiredError) {
|
|
384
443
|
forwardAuthError = formatErrorResponse(409, "invalid_request_error", "Codex thread account affinity expired; start a new session");
|
|
385
444
|
} else if (err instanceof CodexAuthContextError) {
|
|
@@ -431,34 +490,53 @@ export async function handleImages(
|
|
|
431
490
|
const timeoutMs = config.images?.timeoutMs ?? IMAGES_UPSTREAM_TIMEOUT_MS;
|
|
432
491
|
const linkedSignal = signalWithTimeout(timeoutMs, req.signal);
|
|
433
492
|
const sidecarExit = sidecarEnter("images");
|
|
493
|
+
let upstreamResponse: Response | undefined;
|
|
434
494
|
try {
|
|
435
495
|
// Images POSTs create paid, non-idempotent work. One fetch only: no reset retry without a
|
|
436
496
|
// source-proven idempotency contract.
|
|
437
|
-
|
|
497
|
+
upstreamResponse = await fetch(url, {
|
|
438
498
|
method: "POST",
|
|
439
499
|
headers,
|
|
440
500
|
body: JSON.stringify(body),
|
|
441
501
|
signal: linkedSignal.signal,
|
|
502
|
+
// Do not follow a cross-origin 3xx while carrying Codex credentials. Bun strips
|
|
503
|
+
// `Authorization` across origins but forwards nonstandard headers, so
|
|
504
|
+
// `chatgpt-account-id`, `session_id`, and `x-codex-turn-metadata` would reach the
|
|
505
|
+
// redirect target. Verified with a two-server probe. The Responses path and native
|
|
506
|
+
// compact already set this; the credential-bearing sidecars did not.
|
|
507
|
+
redirect: "manual",
|
|
442
508
|
});
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
if (
|
|
448
|
-
|
|
509
|
+
const observed = await readImageResponseBytes(upstreamResponse, {
|
|
510
|
+
maxBytes: IMAGES_RESPONSE_MAX_BYTES,
|
|
511
|
+
signal: linkedSignal.signal,
|
|
512
|
+
});
|
|
513
|
+
if (observed.oversized) {
|
|
514
|
+
forward?.recordOutcome?.(upstreamResponse.status);
|
|
515
|
+
return formatErrorResponse(
|
|
516
|
+
502,
|
|
517
|
+
"upstream_error",
|
|
518
|
+
`image ${endpoint} response too large (exceeded ${IMAGES_RESPONSE_MAX_BYTES} bytes)`,
|
|
519
|
+
);
|
|
449
520
|
}
|
|
450
|
-
forward?.recordOutcome?.(upstreamResponse.status);
|
|
451
521
|
const relayHeaders: Record<string, string> = {};
|
|
452
522
|
const contentType = upstreamResponse.headers.get("content-type");
|
|
453
523
|
if (contentType) relayHeaders["content-type"] = contentType;
|
|
454
|
-
|
|
524
|
+
// Fetch represents 204/205/304 responses with a null body. Preserve that
|
|
525
|
+
// invariant: constructing those statuses with even an empty Uint8Array throws.
|
|
526
|
+
const relayBody = upstreamResponse.body === null ? null : observed.bytes;
|
|
527
|
+
const relayResponse = new Response(relayBody, {
|
|
528
|
+
status: upstreamResponse.status,
|
|
529
|
+
headers: relayHeaders,
|
|
530
|
+
});
|
|
531
|
+
forward?.recordOutcome?.(upstreamResponse.status);
|
|
532
|
+
return relayResponse;
|
|
455
533
|
} catch (err) {
|
|
456
534
|
// Client cancel first: it aborts the linked signal too, and must not be logged as an
|
|
457
535
|
// upstream failure (499 maps to client_closed_request in the request log).
|
|
458
536
|
if (req.signal.aborted) {
|
|
459
537
|
return formatErrorResponse(499, "client_closed_request", `image ${endpoint} request canceled by client`);
|
|
460
538
|
}
|
|
461
|
-
if (err instanceof Error && err.name === "TimeoutError") {
|
|
539
|
+
if (linkedSignal.signal.aborted || (err instanceof Error && err.name === "TimeoutError")) {
|
|
462
540
|
forward?.recordOutcome?.("timeout");
|
|
463
541
|
// codex retries 5xx up to 4 more times; a retried 504 is acceptable for a transient hang.
|
|
464
542
|
return formatErrorResponse(504, "upstream_error", `image ${endpoint} upstream timed out`);
|
|
@@ -472,5 +550,9 @@ export async function handleImages(
|
|
|
472
550
|
} finally {
|
|
473
551
|
sidecarExit();
|
|
474
552
|
linkedSignal.cleanup();
|
|
553
|
+
// If cancellation won before readImageResponseBytes attached its reader, the
|
|
554
|
+
// response still owns an upstream body/socket. Consumed or locked bodies are
|
|
555
|
+
// already owned by the reader and need no second cancellation.
|
|
556
|
+
cancelUnlockedResponseBody(upstreamResponse);
|
|
475
557
|
}
|
|
476
558
|
}
|