@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,621 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider-opt-in repair for Responses-compatible gateways that return sparse
|
|
3
|
+
* lifecycle snapshots (#893): field backfills for missing canonical fields,
|
|
4
|
+
* AND lifecycle event injection so Codex clients actually commit the turn.
|
|
5
|
+
*
|
|
6
|
+
* Field-repair semantics (snapshot fields, item/part backfills, retention
|
|
7
|
+
* bounds) are adopted from PR #928 (0xWinner98) with attribution. The
|
|
8
|
+
* lifecycle-completion layer is new: #928's exact-issue test expects no
|
|
9
|
+
* `output_item.done` and a terminal `output: []` — normalization without
|
|
10
|
+
* commitment, which the canonical bridge contract (src/bridge.ts) shows is
|
|
11
|
+
* not enough for Codex to commit the message.
|
|
12
|
+
*
|
|
13
|
+
* Design:
|
|
14
|
+
* - One stateful tracker per stream. Existing upstream values are always
|
|
15
|
+
* authoritative; only absent or structurally invalid fields are backfilled.
|
|
16
|
+
* - Event injection happens only for items PROVEN open at a terminal event
|
|
17
|
+
* (never for content the gateway closed itself).
|
|
18
|
+
* - Ambiguous, gapped, malformed, oversized, or contradictory shapes taint
|
|
19
|
+
* the tracker: no injections and no output reconstruction afterwards
|
|
20
|
+
* (fail closed; explicit `output: []` from the gateway stays authoritative).
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import type { TranslatorBudget } from "../lib/translator-budget";
|
|
24
|
+
import {
|
|
25
|
+
MAX_COMPLETED_OUTPUT_ITEMS,
|
|
26
|
+
MAX_COMPLETED_OUTPUT_ITEM_SOURCE_BYTES,
|
|
27
|
+
} from "./relay";
|
|
28
|
+
import { sseDataPayload, type SseBlockRewrite } from "./sse-payload-rewrite";
|
|
29
|
+
|
|
30
|
+
const RESPONSE_EVENT_STATUSES: Readonly<Record<string, string>> = {
|
|
31
|
+
"response.created": "in_progress",
|
|
32
|
+
"response.in_progress": "in_progress",
|
|
33
|
+
"response.completed": "completed",
|
|
34
|
+
"response.failed": "failed",
|
|
35
|
+
"response.incomplete": "incomplete",
|
|
36
|
+
"response.queued": "queued",
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
type RequestDefaults = {
|
|
40
|
+
parallelToolCalls: boolean;
|
|
41
|
+
toolChoice: unknown;
|
|
42
|
+
tools: unknown[];
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
46
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function isStructurallyValidToolChoice(value: unknown): boolean {
|
|
50
|
+
return (typeof value === "string" && value.trim().length > 0)
|
|
51
|
+
|| (isPlainObject(value) && typeof value.type === "string" && value.type.trim().length > 0);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function requestDefaults(requestBody: unknown): RequestDefaults {
|
|
55
|
+
const request = isPlainObject(requestBody) ? requestBody : {};
|
|
56
|
+
return {
|
|
57
|
+
parallelToolCalls: typeof request.parallel_tool_calls === "boolean"
|
|
58
|
+
? request.parallel_tool_calls
|
|
59
|
+
: true,
|
|
60
|
+
toolChoice: isStructurallyValidToolChoice(request.tool_choice) ? request.tool_choice : "auto",
|
|
61
|
+
tools: Array.isArray(request.tools) ? request.tools : [],
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function repairOutputTextPart(part: Record<string, unknown>): Record<string, unknown> {
|
|
66
|
+
if (part.type !== "output_text") return part;
|
|
67
|
+
const needsText = typeof part.text !== "string";
|
|
68
|
+
const needsAnnotations = !Array.isArray(part.annotations);
|
|
69
|
+
if (!needsText && !needsAnnotations) return part;
|
|
70
|
+
return {
|
|
71
|
+
...part,
|
|
72
|
+
...(needsText ? { text: "" } : {}),
|
|
73
|
+
...(needsAnnotations ? { annotations: [] } : {}),
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function repairSummaryPart(part: Record<string, unknown>): Record<string, unknown> {
|
|
78
|
+
if (part.type !== "summary_text" || typeof part.text === "string") return part;
|
|
79
|
+
return { ...part, text: "" };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function repairOutputItem(
|
|
83
|
+
item: Record<string, unknown>,
|
|
84
|
+
inferredStatus?: string,
|
|
85
|
+
): Record<string, unknown> {
|
|
86
|
+
let repaired = item;
|
|
87
|
+
let changed = false;
|
|
88
|
+
|
|
89
|
+
if (item.type === "reasoning") {
|
|
90
|
+
const rawSummary = item.summary;
|
|
91
|
+
const summary = Array.isArray(rawSummary)
|
|
92
|
+
? rawSummary.map((part) => isPlainObject(part) ? repairSummaryPart(part) : part)
|
|
93
|
+
: [];
|
|
94
|
+
changed = !Array.isArray(rawSummary)
|
|
95
|
+
|| summary.some((part, index) => part !== rawSummary[index]);
|
|
96
|
+
if (changed) repaired = { ...repaired, summary };
|
|
97
|
+
} else if (item.type === "message") {
|
|
98
|
+
const rawContent = item.content;
|
|
99
|
+
const content = Array.isArray(rawContent)
|
|
100
|
+
? rawContent.map((part) => isPlainObject(part) ? repairOutputTextPart(part) : part)
|
|
101
|
+
: [];
|
|
102
|
+
changed = !Array.isArray(rawContent)
|
|
103
|
+
|| content.some((part, index) => part !== rawContent[index]);
|
|
104
|
+
// Responses output-message roles are the literal "assistant"; input roles are invalid here.
|
|
105
|
+
changed = changed || item.role !== "assistant";
|
|
106
|
+
if (changed) repaired = { ...repaired, content, role: "assistant" };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (inferredStatus && (typeof repaired.status !== "string" || repaired.status.trim().length === 0)) {
|
|
110
|
+
repaired = { ...repaired, status: inferredStatus };
|
|
111
|
+
}
|
|
112
|
+
return repaired;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function repairResponseSnapshot(
|
|
116
|
+
response: Record<string, unknown>,
|
|
117
|
+
defaultStatus: string,
|
|
118
|
+
defaults: RequestDefaults,
|
|
119
|
+
reconstructedOutput?: Record<string, unknown>[],
|
|
120
|
+
): Record<string, unknown> {
|
|
121
|
+
const repaired = { ...response };
|
|
122
|
+
let changed = false;
|
|
123
|
+
const effectiveResponseStatus = typeof response.status === "string" && response.status.trim().length > 0
|
|
124
|
+
? response.status
|
|
125
|
+
: defaultStatus;
|
|
126
|
+
const outputStatus = effectiveResponseStatus === "completed" || effectiveResponseStatus === "incomplete"
|
|
127
|
+
? effectiveResponseStatus
|
|
128
|
+
: undefined;
|
|
129
|
+
|
|
130
|
+
if (reconstructedOutput) {
|
|
131
|
+
repaired.output = reconstructedOutput;
|
|
132
|
+
changed = true;
|
|
133
|
+
} else if (Array.isArray(repaired.output)) {
|
|
134
|
+
const output = repaired.output.map((item) => {
|
|
135
|
+
if (!isPlainObject(item)) return item;
|
|
136
|
+
const next = repairOutputItem(item, outputStatus);
|
|
137
|
+
changed = changed || next !== item;
|
|
138
|
+
return next;
|
|
139
|
+
});
|
|
140
|
+
if (changed) repaired.output = output;
|
|
141
|
+
}
|
|
142
|
+
if (typeof repaired.parallel_tool_calls !== "boolean") {
|
|
143
|
+
repaired.parallel_tool_calls = defaults.parallelToolCalls;
|
|
144
|
+
changed = true;
|
|
145
|
+
}
|
|
146
|
+
if (!isStructurallyValidToolChoice(repaired.tool_choice)) {
|
|
147
|
+
repaired.tool_choice = defaults.toolChoice;
|
|
148
|
+
changed = true;
|
|
149
|
+
}
|
|
150
|
+
if (!Array.isArray(repaired.tools)) {
|
|
151
|
+
repaired.tools = defaults.tools;
|
|
152
|
+
changed = true;
|
|
153
|
+
}
|
|
154
|
+
if (typeof repaired.status !== "string" || repaired.status.trim().length === 0) {
|
|
155
|
+
repaired.status = defaultStatus;
|
|
156
|
+
changed = true;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return changed ? repaired : response;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
type OpenItem = {
|
|
163
|
+
itemId: string;
|
|
164
|
+
outputIndex: number;
|
|
165
|
+
type: string;
|
|
166
|
+
/** Message/reasoning items get lifecycle injections; other types never do. */
|
|
167
|
+
injectable: boolean;
|
|
168
|
+
/** The gateway opened a content part explicitly (or we injected one). */
|
|
169
|
+
contentPartOpen: boolean;
|
|
170
|
+
/** The gateway sent output_text.done for this item. */
|
|
171
|
+
textDone: boolean;
|
|
172
|
+
/** The gateway sent content_part.done for this item. */
|
|
173
|
+
partDone: boolean;
|
|
174
|
+
text: string;
|
|
175
|
+
item: Record<string, unknown>;
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
/** Open-item retention bounds (#893 review): aggregate, not just per-item. */
|
|
179
|
+
const MAX_OPEN_ITEMS = MAX_COMPLETED_OUTPUT_ITEMS;
|
|
180
|
+
const MAX_OPEN_ITEM_AGGREGATE_TEXT_BYTES = MAX_COMPLETED_OUTPUT_ITEM_SOURCE_BYTES;
|
|
181
|
+
|
|
182
|
+
type RetainedOutputItem = {
|
|
183
|
+
item: Record<string, unknown>;
|
|
184
|
+
sourceBytes: number;
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
function jsonBlock(event: Record<string, unknown>): string {
|
|
188
|
+
return `data: ${JSON.stringify(event)}`;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Stateful block rewrite: field backfills + lifecycle completion injection.
|
|
193
|
+
* `budget` bounds retained completed items (reconstruction only).
|
|
194
|
+
*/
|
|
195
|
+
export function createResponsesSnapshotBlockRewrite(
|
|
196
|
+
requestBody?: unknown,
|
|
197
|
+
budget?: TranslatorBudget,
|
|
198
|
+
): SseBlockRewrite {
|
|
199
|
+
const defaults = requestDefaults(requestBody);
|
|
200
|
+
const openItems = new Map<number, OpenItem>();
|
|
201
|
+
const completedItems = new Map<number, RetainedOutputItem>();
|
|
202
|
+
let aggregateItemBytes = 0;
|
|
203
|
+
let aggregateOpenTextBytes = 0;
|
|
204
|
+
let tainted = false;
|
|
205
|
+
|
|
206
|
+
const releaseRetained = (): void => {
|
|
207
|
+
if (aggregateItemBytes > 0) {
|
|
208
|
+
budget?.releaseRetained(aggregateItemBytes, { kind: "retained_collectors" });
|
|
209
|
+
}
|
|
210
|
+
completedItems.clear();
|
|
211
|
+
openItems.clear();
|
|
212
|
+
aggregateItemBytes = 0;
|
|
213
|
+
aggregateOpenTextBytes = 0;
|
|
214
|
+
tainted = false;
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
const taintAndRelease = (): void => {
|
|
218
|
+
// Fail closed means stop RETAINING too: open state and charged collectors
|
|
219
|
+
// are released immediately, and no further state accumulates (#893 review).
|
|
220
|
+
if (aggregateItemBytes > 0) {
|
|
221
|
+
budget?.releaseRetained(aggregateItemBytes, { kind: "retained_collectors" });
|
|
222
|
+
}
|
|
223
|
+
completedItems.clear();
|
|
224
|
+
openItems.clear();
|
|
225
|
+
aggregateItemBytes = 0;
|
|
226
|
+
aggregateOpenTextBytes = 0;
|
|
227
|
+
tainted = true;
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
/** Drop one open item and refund its accumulated text from the aggregate. */
|
|
231
|
+
const closeOpenItem = (index: number): void => {
|
|
232
|
+
const open = openItems.get(index);
|
|
233
|
+
if (!open) return;
|
|
234
|
+
aggregateOpenTextBytes -= Buffer.byteLength(open.text, "utf8");
|
|
235
|
+
openItems.delete(index);
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
const retainCompletedItem = (index: number, item: Record<string, unknown>): void => {
|
|
239
|
+
if (tainted) return; // fail closed means stop retaining (#893 review)
|
|
240
|
+
const sourceBytes = Buffer.byteLength(JSON.stringify(item), "utf8");
|
|
241
|
+
const previous = completedItems.get(index);
|
|
242
|
+
if (sourceBytes > MAX_COMPLETED_OUTPUT_ITEM_SOURCE_BYTES) {
|
|
243
|
+
if (previous) {
|
|
244
|
+
completedItems.delete(index);
|
|
245
|
+
aggregateItemBytes -= previous.sourceBytes;
|
|
246
|
+
budget?.releaseRetained(previous.sourceBytes, { kind: "retained_collectors" });
|
|
247
|
+
}
|
|
248
|
+
taintAndRelease();
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
const retainedDelta = sourceBytes - (previous?.sourceBytes ?? 0);
|
|
252
|
+
if (retainedDelta > 0) {
|
|
253
|
+
budget?.chargeRetained(retainedDelta, { kind: "retained_collectors" });
|
|
254
|
+
} else if (retainedDelta < 0) {
|
|
255
|
+
budget?.releaseRetained(-retainedDelta, { kind: "retained_collectors" });
|
|
256
|
+
}
|
|
257
|
+
completedItems.set(index, { item, sourceBytes });
|
|
258
|
+
aggregateItemBytes += retainedDelta;
|
|
259
|
+
while (completedItems.size > MAX_COMPLETED_OUTPUT_ITEMS
|
|
260
|
+
|| aggregateItemBytes > MAX_COMPLETED_OUTPUT_ITEM_SOURCE_BYTES) {
|
|
261
|
+
let highestIndex = -1;
|
|
262
|
+
for (const retainedIndex of completedItems.keys()) {
|
|
263
|
+
if (retainedIndex > highestIndex) highestIndex = retainedIndex;
|
|
264
|
+
}
|
|
265
|
+
const evicted = completedItems.get(highestIndex);
|
|
266
|
+
if (!evicted) break;
|
|
267
|
+
completedItems.delete(highestIndex);
|
|
268
|
+
aggregateItemBytes -= evicted.sourceBytes;
|
|
269
|
+
budget?.releaseRetained(evicted.sourceBytes, { kind: "retained_collectors" });
|
|
270
|
+
taintAndRelease();
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
const completedItemSnapshot = (open: OpenItem): Record<string, unknown> => {
|
|
276
|
+
if (open.type === "message") {
|
|
277
|
+
return repairOutputItem({
|
|
278
|
+
...open.item,
|
|
279
|
+
status: "completed",
|
|
280
|
+
role: "assistant",
|
|
281
|
+
content: [{ type: "output_text", text: open.text, annotations: [] }],
|
|
282
|
+
}, "completed");
|
|
283
|
+
}
|
|
284
|
+
return repairOutputItem({ ...open.item, status: "completed" }, "completed");
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
const rewrite: SseBlockRewrite = (block: string): readonly string[] => {
|
|
288
|
+
const payload = sseDataPayload(block);
|
|
289
|
+
if (payload === null) return [block];
|
|
290
|
+
let event: unknown;
|
|
291
|
+
try {
|
|
292
|
+
event = JSON.parse(payload);
|
|
293
|
+
} catch {
|
|
294
|
+
return [block];
|
|
295
|
+
}
|
|
296
|
+
if (!isPlainObject(event)) return [block];
|
|
297
|
+
const type = typeof event.type === "string" ? event.type : "";
|
|
298
|
+
let nextEvent: Record<string, unknown> = event;
|
|
299
|
+
let changed = false;
|
|
300
|
+
const outputIndex = Number.isInteger(event.output_index) && (event.output_index as number) >= 0
|
|
301
|
+
? event.output_index as number
|
|
302
|
+
: undefined;
|
|
303
|
+
const itemId = typeof event.item_id === "string" ? event.item_id : undefined;
|
|
304
|
+
|
|
305
|
+
// --- item lifecycle tracking + field backfills -------------------------
|
|
306
|
+
if (type === "response.output_item.added" && isPlainObject(event.item)) {
|
|
307
|
+
const itemType = typeof event.item.type === "string" ? event.item.type : "";
|
|
308
|
+
const id = typeof event.item.id === "string" ? event.item.id : undefined;
|
|
309
|
+
if (outputIndex === undefined || !id) {
|
|
310
|
+
taintAndRelease();
|
|
311
|
+
} else {
|
|
312
|
+
if (openItems.has(outputIndex)) taintAndRelease(); // contradictory reuse of an open index
|
|
313
|
+
if (openItems.size >= MAX_OPEN_ITEMS) taintAndRelease(); // aggregate retention bound
|
|
314
|
+
// Unsupported types (function_call, …) reserve their index but are
|
|
315
|
+
// never injected — a sparse gateway that leaves one open blocks only
|
|
316
|
+
// the terminal reconstruction, not message repair (#893 review).
|
|
317
|
+
const injectable = itemType === "message" || itemType === "reasoning";
|
|
318
|
+
if (!tainted) {
|
|
319
|
+
openItems.set(outputIndex, {
|
|
320
|
+
itemId: id,
|
|
321
|
+
outputIndex,
|
|
322
|
+
type: itemType,
|
|
323
|
+
injectable,
|
|
324
|
+
contentPartOpen: false,
|
|
325
|
+
textDone: false,
|
|
326
|
+
partDone: false,
|
|
327
|
+
text: "",
|
|
328
|
+
item: event.item,
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
const item = repairOutputItem(event.item, "in_progress");
|
|
333
|
+
if (item !== event.item) {
|
|
334
|
+
nextEvent = { ...nextEvent, item };
|
|
335
|
+
changed = true;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
if (type === "response.output_item.done" && !isPlainObject(event.item)) {
|
|
340
|
+
taintAndRelease();
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
if (type === "response.output_item.done" && isPlainObject(event.item)) {
|
|
344
|
+
const item = repairOutputItem(event.item, "completed");
|
|
345
|
+
if (item !== event.item) {
|
|
346
|
+
nextEvent = { ...nextEvent, item };
|
|
347
|
+
changed = true;
|
|
348
|
+
}
|
|
349
|
+
// Identity correlation: a done event only closes the tracked item when
|
|
350
|
+
// id AND type agree — a mismatched done is a contradictory stream and
|
|
351
|
+
// must go fail-closed rather than close/reconstruct the wrong item.
|
|
352
|
+
const tracked = outputIndex !== undefined ? openItems.get(outputIndex) : undefined;
|
|
353
|
+
const doneId = typeof event.item.id === "string" ? event.item.id : undefined;
|
|
354
|
+
const doneType = typeof event.item.type === "string" ? event.item.type : undefined;
|
|
355
|
+
if (tracked && (doneId !== tracked.itemId || doneType !== tracked.type)) {
|
|
356
|
+
taintAndRelease();
|
|
357
|
+
return [changed ? jsonBlock(nextEvent) : block];
|
|
358
|
+
}
|
|
359
|
+
if (outputIndex !== undefined && typeof item.type === "string" && item.type.trim().length > 0) {
|
|
360
|
+
closeOpenItem(outputIndex);
|
|
361
|
+
retainCompletedItem(outputIndex, item);
|
|
362
|
+
} else {
|
|
363
|
+
taintAndRelease();
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
if ((type === "response.content_part.added" || type === "response.content_part.done")
|
|
368
|
+
&& isPlainObject(event.part)) {
|
|
369
|
+
if (outputIndex !== undefined) {
|
|
370
|
+
const open = openItems.get(outputIndex);
|
|
371
|
+
// A PRESENT-but-mismatched item_id is contradictory lifecycle evidence,
|
|
372
|
+
// exactly like output_item.done and output_text.done: the stream is
|
|
373
|
+
// telling us our identity model for this index is wrong. Merely
|
|
374
|
+
// ignoring it left the item open and let the terminal fabricate a full
|
|
375
|
+
// closure sequence (content_part.added → output_text.done →
|
|
376
|
+
// content_part.done → output_item.done) on top of a stream we do not
|
|
377
|
+
// understand. Go fail-closed instead. An OMITTED item_id stays
|
|
378
|
+
// legitimate and is still correlated by output_index.
|
|
379
|
+
if (open && itemId !== undefined && itemId !== open.itemId) {
|
|
380
|
+
taintAndRelease();
|
|
381
|
+
return [changed ? jsonBlock(nextEvent) : block];
|
|
382
|
+
}
|
|
383
|
+
// Correlate by item_id when present: a mismatched event must not
|
|
384
|
+
// mutate (or suppress injections for) the tracked item (#893 review).
|
|
385
|
+
if (open && (itemId === undefined || itemId === open.itemId)) {
|
|
386
|
+
if (type === "response.content_part.added") open.contentPartOpen = true;
|
|
387
|
+
else open.partDone = true;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
const part = repairOutputTextPart(event.part);
|
|
391
|
+
if (part !== event.part) {
|
|
392
|
+
nextEvent = { ...nextEvent, part };
|
|
393
|
+
changed = true;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
if ((type === "response.reasoning_summary_part.added"
|
|
398
|
+
|| type === "response.reasoning_summary_part.done") && isPlainObject(event.part)) {
|
|
399
|
+
const part = repairSummaryPart(event.part);
|
|
400
|
+
if (part !== event.part) {
|
|
401
|
+
nextEvent = { ...nextEvent, part };
|
|
402
|
+
changed = true;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
if ((type === "response.output_text.delta" || type === "response.output_text.done")
|
|
407
|
+
&& !Array.isArray(event.logprobs)) {
|
|
408
|
+
nextEvent = { ...nextEvent, logprobs: [] };
|
|
409
|
+
changed = true;
|
|
410
|
+
}
|
|
411
|
+
if (type === "response.output_text.delta" && typeof event.delta === "string" && outputIndex !== undefined) {
|
|
412
|
+
const open = openItems.get(outputIndex);
|
|
413
|
+
// Same identity contract as the *.done terminals: a present-but-foreign
|
|
414
|
+
// item_id on a tracked index means our model of this index is wrong, and
|
|
415
|
+
// reconstructing from the text we DID accept would ship a message the
|
|
416
|
+
// upstream never assembled that way.
|
|
417
|
+
if (open && itemId !== undefined && itemId !== open.itemId) {
|
|
418
|
+
taintAndRelease();
|
|
419
|
+
return [changed ? jsonBlock(nextEvent) : block];
|
|
420
|
+
}
|
|
421
|
+
if (open && (itemId === undefined || itemId === open.itemId)) {
|
|
422
|
+
const deltaBytes = Buffer.byteLength(event.delta, "utf8");
|
|
423
|
+
open.text += event.delta;
|
|
424
|
+
aggregateOpenTextBytes += deltaBytes;
|
|
425
|
+
// Unbounded text accumulation is a retention hole (#893 review):
|
|
426
|
+
// overshoot per-item or aggregate caps and the stream goes fail-closed.
|
|
427
|
+
if (Buffer.byteLength(open.text, "utf8") > MAX_COMPLETED_OUTPUT_ITEM_SOURCE_BYTES
|
|
428
|
+
|| aggregateOpenTextBytes > MAX_OPEN_ITEM_AGGREGATE_TEXT_BYTES) {
|
|
429
|
+
taintAndRelease();
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
if (type === "response.output_text.done") {
|
|
434
|
+
if (outputIndex !== undefined) {
|
|
435
|
+
const open = openItems.get(outputIndex);
|
|
436
|
+
// Identity correlation, same contract as output_item.done above: a
|
|
437
|
+
// text-done carrying a DIFFERENT item_id for a tracked index is a
|
|
438
|
+
// contradictory stream. Silently ignoring it used to leave the item
|
|
439
|
+
// open, so the terminal then synthesized a second output_text.done and
|
|
440
|
+
// output_item.done — a double close built on a stream we do not
|
|
441
|
+
// understand (#1025 review blocker 2). Go fail-closed instead: forward
|
|
442
|
+
// the block untouched and stop injecting for the rest of the stream.
|
|
443
|
+
if (open && itemId !== undefined && itemId !== open.itemId) {
|
|
444
|
+
taintAndRelease();
|
|
445
|
+
return [changed ? jsonBlock(nextEvent) : block];
|
|
446
|
+
}
|
|
447
|
+
if (open && (itemId === undefined || itemId === open.itemId)) {
|
|
448
|
+
open.textDone = true;
|
|
449
|
+
if (typeof event.text === "string") {
|
|
450
|
+
// Replacement must keep the aggregate honest and bounded, the
|
|
451
|
+
// same as delta accumulation (#893 review).
|
|
452
|
+
const previousBytes = Buffer.byteLength(open.text, "utf8");
|
|
453
|
+
const nextBytes = Buffer.byteLength(event.text, "utf8");
|
|
454
|
+
open.text = event.text;
|
|
455
|
+
aggregateOpenTextBytes += nextBytes - previousBytes;
|
|
456
|
+
if (nextBytes > MAX_COMPLETED_OUTPUT_ITEM_SOURCE_BYTES
|
|
457
|
+
|| aggregateOpenTextBytes > MAX_OPEN_ITEM_AGGREGATE_TEXT_BYTES) {
|
|
458
|
+
taintAndRelease();
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
if (typeof event.text !== "string") {
|
|
464
|
+
nextEvent = { ...nextEvent, text: "" };
|
|
465
|
+
changed = true;
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
// --- lifecycle response snapshots --------------------------------------
|
|
470
|
+
const responseStatus = Object.prototype.hasOwnProperty.call(RESPONSE_EVENT_STATUSES, type)
|
|
471
|
+
? RESPONSE_EVENT_STATUSES[type]
|
|
472
|
+
: undefined;
|
|
473
|
+
const isTerminal = type === "response.completed"
|
|
474
|
+
|| type === "response.failed"
|
|
475
|
+
|| type === "response.incomplete";
|
|
476
|
+
|
|
477
|
+
if (responseStatus && isPlainObject(event.response)) {
|
|
478
|
+
// Terminal output reconstruction stays in the injection layer below;
|
|
479
|
+
// here the snapshot only gets field backfills.
|
|
480
|
+
const response = repairResponseSnapshot(event.response, responseStatus, defaults);
|
|
481
|
+
if (response !== event.response) {
|
|
482
|
+
nextEvent = { ...nextEvent, response };
|
|
483
|
+
changed = true;
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// --- lifecycle completion injection ------------------------------------
|
|
488
|
+
// Only response.completed justifies synthesized closing events: a failed
|
|
489
|
+
// or incomplete terminal must never fabricate completed output (#893 review).
|
|
490
|
+
const isCompletedTerminal = type === "response.completed";
|
|
491
|
+
const out: string[] = [];
|
|
492
|
+
if (!tainted) {
|
|
493
|
+
// A text delta for an item whose content part was never opened needs the
|
|
494
|
+
// opening event first, or Codex never binds the text to a committed part.
|
|
495
|
+
if (type === "response.output_text.delta" && outputIndex !== undefined) {
|
|
496
|
+
const open = openItems.get(outputIndex);
|
|
497
|
+
if (open && open.type === "message" && !open.contentPartOpen) {
|
|
498
|
+
out.push(jsonBlock({
|
|
499
|
+
type: "response.content_part.added",
|
|
500
|
+
item_id: open.itemId,
|
|
501
|
+
output_index: open.outputIndex,
|
|
502
|
+
content_index: 0,
|
|
503
|
+
part: { type: "output_text", text: "", annotations: [] },
|
|
504
|
+
}));
|
|
505
|
+
open.contentPartOpen = true;
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
if (isCompletedTerminal && openItems.size > 0) {
|
|
510
|
+
const closing = [...openItems.values()].sort((a, b) => a.outputIndex - b.outputIndex);
|
|
511
|
+
for (const open of closing) {
|
|
512
|
+
if (open.injectable && open.type === "message") {
|
|
513
|
+
if (!open.contentPartOpen) {
|
|
514
|
+
out.push(jsonBlock({
|
|
515
|
+
type: "response.content_part.added",
|
|
516
|
+
item_id: open.itemId,
|
|
517
|
+
output_index: open.outputIndex,
|
|
518
|
+
content_index: 0,
|
|
519
|
+
part: { type: "output_text", text: "", annotations: [] },
|
|
520
|
+
}));
|
|
521
|
+
open.contentPartOpen = true;
|
|
522
|
+
}
|
|
523
|
+
if (!open.textDone) {
|
|
524
|
+
out.push(jsonBlock({
|
|
525
|
+
type: "response.output_text.done",
|
|
526
|
+
item_id: open.itemId,
|
|
527
|
+
output_index: open.outputIndex,
|
|
528
|
+
content_index: 0,
|
|
529
|
+
logprobs: [],
|
|
530
|
+
text: open.text,
|
|
531
|
+
}));
|
|
532
|
+
open.textDone = true;
|
|
533
|
+
}
|
|
534
|
+
if (!open.partDone) {
|
|
535
|
+
out.push(jsonBlock({
|
|
536
|
+
type: "response.content_part.done",
|
|
537
|
+
item_id: open.itemId,
|
|
538
|
+
output_index: open.outputIndex,
|
|
539
|
+
content_index: 0,
|
|
540
|
+
part: { type: "output_text", text: open.text, annotations: [] },
|
|
541
|
+
}));
|
|
542
|
+
open.partDone = true;
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
if (!open.injectable) continue; // never fabricate completions for other types
|
|
546
|
+
const snapshot = completedItemSnapshot(open);
|
|
547
|
+
out.push(jsonBlock({
|
|
548
|
+
type: "response.output_item.done",
|
|
549
|
+
output_index: open.outputIndex,
|
|
550
|
+
item: snapshot,
|
|
551
|
+
}));
|
|
552
|
+
closeOpenItem(open.outputIndex);
|
|
553
|
+
retainCompletedItem(open.outputIndex, snapshot);
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
// Terminal output reconstruction/canonical backfill: an explicit
|
|
559
|
+
// `output: []` is authoritative; absent or malformed output on a
|
|
560
|
+
// completed terminal becomes the retained items or the canonical empty
|
|
561
|
+
// list (#893 review). Leftover non-injectable open items block
|
|
562
|
+
// reconstruction only, not the message closing above.
|
|
563
|
+
if (isCompletedTerminal && !tainted && isPlainObject(nextEvent.response)) {
|
|
564
|
+
const response = nextEvent.response;
|
|
565
|
+
const outputValue = (response as Record<string, unknown>).output;
|
|
566
|
+
const outputExplicitArray = Array.isArray(outputValue);
|
|
567
|
+
if (!outputExplicitArray) {
|
|
568
|
+
const injectableOpen = [...openItems.values()].filter(open => open.injectable);
|
|
569
|
+
const nonInjectableOpen = [...openItems.values()].filter(open => !open.injectable);
|
|
570
|
+
if (completedItems.size > 0 && injectableOpen.length === 0 && nonInjectableOpen.length === 0) {
|
|
571
|
+
const ordered = [...completedItems.entries()].sort(([left], [right]) => left - right);
|
|
572
|
+
if (ordered.every(([index], position) => index === position)) {
|
|
573
|
+
nextEvent = {
|
|
574
|
+
...nextEvent,
|
|
575
|
+
response: { ...response, output: ordered.map(([, retained]) => retained.item) },
|
|
576
|
+
};
|
|
577
|
+
changed = true;
|
|
578
|
+
} else {
|
|
579
|
+
// A gap means at least one completed item is missing. Never compact
|
|
580
|
+
// later indexes into a shorter array that only appears complete.
|
|
581
|
+
tainted = true;
|
|
582
|
+
}
|
|
583
|
+
} else if (completedItems.size === 0 && openItems.size === 0) {
|
|
584
|
+
nextEvent = { ...nextEvent, response: { ...response, output: [] } };
|
|
585
|
+
changed = true;
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
out.push(changed ? jsonBlock(nextEvent) : block);
|
|
591
|
+
if (isTerminal) releaseRetained();
|
|
592
|
+
return out;
|
|
593
|
+
};
|
|
594
|
+
// Relays call this on every teardown path (terminal, EOF, cancel, error):
|
|
595
|
+
// retained collectors and open-item state never outlive the stream.
|
|
596
|
+
rewrite.dispose = releaseRetained;
|
|
597
|
+
return rewrite;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
/** Repair a non-streaming Responses JSON object without changing raw inspection state. */
|
|
601
|
+
export function repairResponsesSnapshotJson(payload: string, requestBody?: unknown): string {
|
|
602
|
+
let response: unknown;
|
|
603
|
+
try {
|
|
604
|
+
response = JSON.parse(payload);
|
|
605
|
+
} catch {
|
|
606
|
+
return payload;
|
|
607
|
+
}
|
|
608
|
+
if (!isPlainObject(response)) return payload;
|
|
609
|
+
// Canonical output for non-stream completed responses: absent or malformed
|
|
610
|
+
// output becomes the canonical empty list; explicit arrays stay authoritative.
|
|
611
|
+
if (!Array.isArray(response.output)) {
|
|
612
|
+
const repaired = repairResponseSnapshot({ ...response, output: [] }, "completed", requestDefaults(requestBody));
|
|
613
|
+
return JSON.stringify(repaired);
|
|
614
|
+
}
|
|
615
|
+
const repaired = repairResponseSnapshot(response, "completed", requestDefaults(requestBody));
|
|
616
|
+
return repaired === response ? payload : JSON.stringify(repaired);
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
export function hasResponsesSnapshotRepair(enabled: boolean | undefined): enabled is true {
|
|
620
|
+
return enabled === true;
|
|
621
|
+
}
|