@iislee/opencodex 2.11.0 → 2.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS_INSTALL.md +109 -0
- package/README.md +114 -19
- package/bin/ocx.mjs +164 -36
- package/bin/package-main.mjs +1 -1
- package/gui/dist/assets/index-BF38heuV.js +104 -0
- package/gui/dist/assets/index-DMiI18Kv.css +1 -0
- package/gui/dist/index.html +2 -2
- package/gui/dist/provider-icons/alibaba-color.svg +1 -1
- package/gui/dist/provider-icons/antigravity-color.svg +1 -1
- package/gui/dist/provider-icons/claude-color.svg +1 -1
- package/gui/dist/provider-icons/cline-color.svg +16 -0
- package/gui/dist/provider-icons/cloudflare-ai-gateway-color.svg +1 -1
- package/gui/dist/provider-icons/commandcode-color.svg +1 -0
- package/gui/dist/provider-icons/copilot-color.svg +1 -1
- package/gui/dist/provider-icons/cursor-color.svg +1 -1
- package/gui/dist/provider-icons/deepseek-color.svg +1 -1
- package/gui/dist/provider-icons/firepass-color.svg +1 -1
- package/gui/dist/provider-icons/fireworks-color.svg +1 -1
- package/gui/dist/provider-icons/gemini-color.svg +1 -1
- package/gui/dist/provider-icons/github-copilot-color.svg +1 -1
- package/gui/dist/provider-icons/gitlab-duo-color.svg +1 -1
- package/gui/dist/provider-icons/grok.svg +1 -1
- package/gui/dist/provider-icons/groq-color.svg +1 -1
- package/gui/dist/provider-icons/huggingface-color.svg +1 -1
- package/gui/dist/provider-icons/kimi-color.svg +1 -1
- package/gui/dist/provider-icons/kiro-color.svg +2 -2
- package/gui/dist/provider-icons/lm-studio-color.svg +1 -1
- package/gui/dist/provider-icons/mistral-color.svg +1 -1
- package/gui/dist/provider-icons/moonshot-color.svg +1 -1
- package/gui/dist/provider-icons/nvidia-color.svg +1 -1
- package/gui/dist/provider-icons/ollama-color.svg +1 -1
- package/gui/dist/provider-icons/openai.svg +1 -1
- package/gui/dist/provider-icons/opencode.svg +2 -1
- package/gui/dist/provider-icons/openrouter-color.svg +1 -1
- package/gui/dist/provider-icons/pi.svg +2 -2
- package/gui/dist/provider-icons/qianfan-color.svg +1 -1
- package/gui/dist/provider-icons/qwen-portal-color.svg +1 -1
- package/gui/dist/provider-icons/vercel-ai-gateway-color.svg +1 -1
- package/gui/dist/provider-icons/vllm-color.svg +1 -1
- package/gui/dist/provider-icons/xiaomi-color.svg +1 -1
- package/package.json +19 -10
- package/src/adapters/anthropic-output-schema.ts +137 -0
- package/src/adapters/anthropic.ts +376 -52
- package/src/adapters/base.ts +54 -7
- package/src/adapters/client-fingerprint.ts +18 -12
- package/src/adapters/cline-pass-deepseek-v4-tool-replay.ts +69 -0
- package/src/adapters/command-code.ts +601 -0
- package/src/adapters/cursor/checkpoint-store.ts +303 -0
- package/src/adapters/cursor/cursor-errors.ts +129 -6
- package/src/adapters/cursor/discovery.ts +61 -4
- package/src/adapters/cursor/effort-map.ts +27 -3
- package/src/adapters/cursor/framing.ts +39 -0
- package/src/adapters/cursor/h2-pool.ts +123 -0
- package/src/adapters/cursor/http1-bidi.ts +361 -0
- package/src/adapters/cursor/images.ts +704 -0
- package/src/adapters/cursor/live-models.ts +174 -58
- package/src/adapters/cursor/live-transport.ts +609 -170
- package/src/adapters/cursor/native-exec-common.ts +23 -2
- package/src/adapters/cursor/native-exec-fs.ts +10 -7
- package/src/adapters/cursor/native-exec-network.ts +1 -1
- package/src/adapters/cursor/native-exec-shell.ts +0 -1
- package/src/adapters/cursor/native-exec.ts +101 -14
- package/src/adapters/cursor/protobuf-events.ts +829 -11
- package/src/adapters/cursor/protobuf-request.ts +383 -65
- package/src/adapters/cursor/request-builder.ts +237 -31
- package/src/adapters/cursor/tool-definitions.ts +242 -12
- package/src/adapters/cursor/tool-result-normalize.ts +92 -0
- package/src/adapters/cursor/transport.ts +22 -0
- package/src/adapters/cursor/types.ts +28 -1
- package/src/adapters/cursor.ts +133 -7
- package/src/adapters/google-antigravity-replay.ts +558 -34
- package/src/adapters/google-antigravity-wire.ts +43 -10
- package/src/adapters/google-http.ts +38 -10
- package/src/adapters/google-tool-schema.ts +80 -15
- package/src/adapters/google-truncation.ts +11 -0
- package/src/adapters/google.ts +618 -74
- package/src/adapters/identity.ts +39 -6
- package/src/adapters/kiro-errors.ts +11 -0
- package/src/adapters/kiro-events.ts +19 -1
- package/src/adapters/kiro-thinking.ts +18 -2
- package/src/adapters/kiro-tools.ts +10 -1
- package/src/adapters/kiro.ts +92 -54
- package/src/adapters/mimo-free.ts +17 -0
- package/src/adapters/openai-chat-url.ts +11 -0
- package/src/adapters/openai-chat.ts +1311 -336
- package/src/adapters/openai-responses-url.ts +16 -0
- package/src/adapters/openai-responses.ts +830 -56
- package/src/adapters/registry.ts +175 -0
- package/src/adapters/responses-tool-schema.ts +67 -0
- package/src/adapters/tool-call-id.ts +119 -0
- package/src/adapters/tool-catalog-nudge.ts +104 -21
- package/src/adapters/xai-web-search.ts +185 -0
- package/src/bridge.ts +428 -61
- package/src/chat/inbound.ts +43 -19
- package/src/chat/outbound.ts +82 -26
- package/src/claude/agents-inject.ts +32 -9
- package/src/claude/context-windows.ts +21 -5
- package/src/claude/desktop-3p.ts +243 -9
- package/src/claude/gateway-cache.ts +41 -4
- package/src/claude/inbound.ts +72 -3
- package/src/claude/model-info.ts +38 -15
- package/src/claude/outbound.ts +70 -16
- package/src/cli/account-api.ts +35 -1
- package/src/cli/account-auth.ts +33 -6
- package/src/cli/account-catalog-refresh.ts +14 -0
- package/src/cli/account-extended.ts +389 -2
- package/src/cli/account-main.ts +317 -0
- package/src/cli/account.ts +30 -6
- package/src/cli/agent.ts +45 -1
- package/src/cli/claude-agent-startup-sync.ts +73 -0
- package/src/cli/claude-desktop.ts +29 -4
- package/src/cli/claude.ts +103 -34
- package/src/cli/codex-log-guard-doctor.ts +103 -0
- package/src/cli/codex-shim-autorestore.ts +2 -0
- package/src/cli/codex-shim-readiness.ts +76 -0
- package/src/cli/combo.ts +8 -0
- package/src/cli/config-command.ts +74 -10
- package/src/cli/dispatch.ts +593 -0
- package/src/cli/doctor.ts +315 -43
- package/src/cli/ensure-desired-integrations.ts +152 -0
- package/src/cli/export-command.ts +46 -20
- package/src/cli/help.ts +20 -271
- package/src/cli/index.ts +378 -519
- package/src/cli/init.ts +4 -17
- package/src/cli/integrations.ts +120 -2
- package/src/cli/lab.ts +607 -0
- package/src/cli/launcher-context.ts +77 -0
- package/src/cli/minimax.ts +497 -0
- package/src/cli/models-runtime.ts +35 -2
- package/src/cli/models.ts +100 -14
- package/src/cli/observe.ts +92 -3
- package/src/cli/opencode.ts +4 -2
- package/src/cli/provider-runtime.ts +18 -1
- package/src/cli/provider.ts +24 -3
- package/src/cli/ready.ts +301 -0
- package/src/cli/registry.ts +437 -0
- package/src/cli/root.ts +86 -0
- package/src/cli/route-policy.ts +92 -0
- package/src/cli/runtime-api.ts +6 -3
- package/src/cli/star-prompt.ts +71 -15
- package/src/cli/status.ts +10 -3
- package/src/cli/system-restart-client.ts +146 -0
- package/src/cli/tray-proxy.ts +153 -6
- package/src/cli/v2.ts +105 -10
- package/src/cli.ts +1 -1
- package/src/clients/config-export.ts +1358 -21
- package/src/codex/account-label.ts +14 -1
- package/src/codex/account-lifecycle.ts +130 -13
- package/src/codex/account-namespaces.ts +49 -3
- package/src/codex/account-priority.ts +83 -0
- package/src/codex/account-store.ts +29 -2
- package/src/codex/account-usability.ts +25 -2
- package/src/codex/admission.ts +256 -0
- package/src/codex/affinity-debug.ts +162 -0
- package/src/codex/app-server-processes.ts +493 -106
- package/src/codex/app-server-restart-service.ts +232 -0
- package/src/codex/auth-api.ts +849 -242
- package/src/codex/auth-collision.ts +5 -3
- package/src/codex/auth-context.ts +345 -32
- package/src/codex/autostart-health.ts +8 -1
- package/src/codex/catalog/account-models.ts +67 -0
- package/src/codex/catalog/aggregation.ts +68 -10
- package/src/codex/catalog/bundled.ts +331 -33
- package/src/codex/catalog/effort.ts +121 -30
- package/src/codex/catalog/filesystem-evidence.ts +302 -0
- package/src/codex/catalog/kinds.ts +2 -0
- package/src/codex/catalog/metadata.ts +529 -45
- package/src/codex/catalog/native-models.ts +72 -0
- package/src/codex/catalog/parsing.ts +224 -30
- package/src/codex/catalog/provider-fetch.ts +1460 -134
- package/src/codex/catalog/sync.ts +1449 -186
- package/src/codex/catalog-admission.ts +199 -0
- package/src/codex/catalog-refresh-status.ts +105 -0
- package/src/codex/catalog-write-serialization.ts +242 -0
- package/src/codex/catalog.ts +6 -3
- package/src/codex/codex-write-lock.ts +384 -0
- package/src/codex/convergence-types.ts +614 -0
- package/src/codex/convergence.ts +651 -0
- package/src/codex/coordinator-doctor.ts +332 -0
- package/src/codex/custom-model-catalog-migration.ts +176 -0
- package/src/codex/desired-state.ts +230 -0
- package/src/codex/features.ts +636 -39
- package/src/codex/generation.ts +202 -0
- package/src/codex/history-job.ts +407 -0
- package/src/codex/history-lock.ts +242 -0
- package/src/codex/history-migration-guardian.ts +26 -20
- package/src/codex/history-provider.ts +231 -28
- package/src/codex/history-transition.ts +105 -0
- package/src/codex/history-worker.ts +220 -0
- package/src/codex/inject-coordination.ts +290 -0
- package/src/codex/inject.ts +1073 -152
- package/src/codex/injected-marker.ts +37 -3
- package/src/codex/integration-record.ts +266 -0
- package/src/codex/internal/catalog-writer.ts +203 -0
- package/src/codex/internal/history-writer.ts +80 -0
- package/src/codex/journal.ts +66 -4
- package/src/codex/log-guard/inspect.ts +506 -0
- package/src/codex/log-guard/lock.ts +150 -0
- package/src/codex/log-guard/maintenance.ts +403 -0
- package/src/codex/log-guard/path-safety.ts +88 -0
- package/src/codex/log-guard/policy.ts +44 -0
- package/src/codex/log-guard/processes.ts +205 -0
- package/src/codex/log-guard/protection.ts +489 -0
- package/src/codex/log-guard/sqlite-errors.ts +9 -0
- package/src/codex/main-account-cache.ts +24 -0
- package/src/codex/main-account.ts +29 -1
- package/src/codex/management-convergence.ts +167 -0
- package/src/codex/model-cache.ts +56 -10
- package/src/codex/model-entitlements.ts +353 -0
- package/src/codex/native-main-admission.ts +47 -0
- package/src/codex/native-main-auth-temp.ts +187 -0
- package/src/codex/native-main-claim.ts +178 -0
- package/src/codex/native-main-lock-file.ts +162 -0
- package/src/codex/native-main-owner.ts +329 -0
- package/src/codex/native-profile-api.ts +247 -0
- package/src/codex/native-profile-manager.ts +1531 -0
- package/src/codex/native-profile-processes.ts +121 -0
- package/src/codex/native-profile-recovery.ts +99 -0
- package/src/codex/native-profile-stage-store.ts +387 -0
- package/src/codex/native-profile-startup.ts +492 -0
- package/src/codex/native-profile-store.ts +855 -0
- package/src/codex/native-profile-types.ts +120 -0
- package/src/codex/native-residue.ts +682 -0
- package/src/codex/paths.ts +80 -1
- package/src/codex/plan-from-token.ts +140 -0
- package/src/codex/plan.ts +40 -0
- package/src/codex/plugins-doctor.ts +1 -1
- package/src/codex/pool-rotation.ts +74 -4
- package/src/codex/project-config-warnings.ts +20 -6
- package/src/codex/prompt-journal.ts +352 -0
- package/src/codex/prompt-layers.ts +967 -0
- package/src/codex/prompt-lock.ts +143 -0
- package/src/codex/quota-rejection.ts +298 -0
- package/src/codex/quota.ts +175 -13
- package/src/codex/refresh.ts +11 -2
- package/src/codex/reset-credit-recovery.ts +1044 -0
- package/src/codex/routing.ts +505 -94
- package/src/codex/runtime.ts +159 -38
- package/src/codex/shim.ts +1009 -28
- package/src/codex/subagent-model-fallback.ts +350 -35
- package/src/codex/sync.ts +191 -2
- package/src/codex/transition-state.ts +612 -0
- package/src/codex/upstream-host-health.ts +368 -0
- package/src/codex/user-identity.ts +557 -0
- package/src/codex/warmup.ts +187 -81
- package/src/codex/write-coordination.ts +114 -0
- package/src/combos/failover.ts +20 -0
- package/src/combos/index.ts +4 -0
- package/src/combos/request.ts +32 -0
- package/src/combos/types.ts +81 -9
- package/src/config/provider-name.ts +24 -0
- package/src/config.ts +1762 -140
- package/src/generated/compatibility-version.json +3164 -0
- package/src/generated/{jawcode-model-metadata.ts → model-metadata.ts} +19 -17
- package/src/grok/inject.ts +16 -5
- package/src/grok/inspect.ts +45 -0
- package/src/grok/sync.ts +2 -2
- package/src/images/loop.ts +152 -29
- package/src/images/plan.ts +23 -13
- package/src/integrations/config-io.ts +269 -0
- package/src/integrations/journal.ts +315 -0
- package/src/integrations/merge.ts +135 -0
- package/src/integrations/mutation-flight.ts +71 -0
- package/src/integrations/native/ownership-preflight.ts +202 -0
- package/src/integrations/omp-yaml-source.ts +358 -0
- package/src/integrations/owned-refresh.ts +74 -0
- package/src/integrations/ownership.ts +111 -0
- package/src/integrations/registry.ts +159 -0
- package/src/integrations/serialize.ts +314 -0
- package/src/integrations/state.ts +361 -0
- package/src/integrations/store.ts +103 -0
- package/src/integrations/writer-lock.ts +98 -0
- package/src/integrations/writer.ts +691 -0
- package/src/lab/artifacts/sanitize.ts +586 -0
- package/src/lab/artifacts/secure-fs.ts +475 -0
- package/src/lab/artifacts/store.ts +310 -0
- package/src/lab/automation/budgets.ts +78 -0
- package/src/lab/automation/config-persistence.ts +256 -0
- package/src/lab/automation/constants.ts +39 -0
- package/src/lab/automation/cooldown.ts +103 -0
- package/src/lab/automation/dispatch.ts +211 -0
- package/src/lab/automation/index.ts +13 -0
- package/src/lab/automation/orchestrator.ts +499 -0
- package/src/lab/automation/persistence.ts +512 -0
- package/src/lab/automation/planner.ts +371 -0
- package/src/lab/automation/policy.ts +136 -0
- package/src/lab/automation/queue.ts +191 -0
- package/src/lab/automation/recovery.ts +24 -0
- package/src/lab/automation/route-context.ts +21 -0
- package/src/lab/automation/run-key.ts +44 -0
- package/src/lab/automation/runs-query.ts +34 -0
- package/src/lab/automation/types.ts +160 -0
- package/src/lab/conformance/assertion.ts +325 -0
- package/src/lab/conformance/digest.ts +22 -0
- package/src/lab/conformance/executor.ts +741 -0
- package/src/lab/conformance/fixture-provider.ts +27 -0
- package/src/lab/conformance/fixtures/live-v1-cases.json +175 -0
- package/src/lab/conformance/fixtures/protocol-v1-cases.json +461 -0
- package/src/lab/conformance/harness-budget.ts +47 -0
- package/src/lab/conformance/index.ts +5 -0
- package/src/lab/conformance/jcs.ts +64 -0
- package/src/lab/conformance/json-pointer.ts +39 -0
- package/src/lab/conformance/manifest.ts +180 -0
- package/src/lab/conformance/mcp-stub.ts +179 -0
- package/src/lab/conformance/negative-controls.ts +164 -0
- package/src/lab/conformance/observation.ts +355 -0
- package/src/lab/conformance/runner.ts +68 -0
- package/src/lab/conformance/sse-normalize.ts +59 -0
- package/src/lab/conformance/suite-manifest.ts +78 -0
- package/src/lab/conformance/types.ts +214 -0
- package/src/lab/constants.ts +126 -0
- package/src/lab/digest.ts +64 -0
- package/src/lab/events/errors.ts +9 -0
- package/src/lab/events/limits.ts +117 -0
- package/src/lab/events/types.ts +229 -0
- package/src/lab/events/validate.ts +781 -0
- package/src/lab/fabric/constants.ts +40 -0
- package/src/lab/fabric/executor.ts +492 -0
- package/src/lab/fabric/index.ts +80 -0
- package/src/lab/fabric/manifest.ts +222 -0
- package/src/lab/fabric/observe.ts +489 -0
- package/src/lab/fabric/patch.ts +79 -0
- package/src/lab/fabric/producer-child.ts +139 -0
- package/src/lab/fabric/producer-isolate.ts +276 -0
- package/src/lab/fabric/producer-protocol.ts +61 -0
- package/src/lab/fabric/scratch.ts +439 -0
- package/src/lab/fabric/subject.ts +106 -0
- package/src/lab/fabric/types.ts +134 -0
- package/src/lab/fabric/verifier.ts +98 -0
- package/src/lab/index.ts +54 -0
- package/src/lab/ledger/artifact-refs.ts +127 -0
- package/src/lab/ledger/invalidation.ts +136 -0
- package/src/lab/ledger/purge.ts +310 -0
- package/src/lab/ledger/store.ts +532 -0
- package/src/lab/live/credential-lease.ts +53 -0
- package/src/lab/live/destination.ts +155 -0
- package/src/lab/live/executor.ts +336 -0
- package/src/lab/live/inert-tools.ts +56 -0
- package/src/lab/live/manifest.ts +85 -0
- package/src/lab/live/mcp-loopback.ts +57 -0
- package/src/lab/live/runner.ts +19 -0
- package/src/lab/live/sandbox.ts +61 -0
- package/src/lab/live/suite-manifest.ts +41 -0
- package/src/lab/live/transport.ts +118 -0
- package/src/lab/live/types.ts +197 -0
- package/src/lab/observe/from-conformance.ts +301 -0
- package/src/lab/observe/from-live.ts +117 -0
- package/src/lab/paths.ts +153 -0
- package/src/lab/projection/rebuild.ts +495 -0
- package/src/lab/projection/schema.ts +135 -0
- package/src/lab/projection/verdicts.ts +474 -0
- package/src/lab/projection/verification.ts +412 -0
- package/src/lab/public/bundle.ts +217 -0
- package/src/lab/public/community-authority.ts +175 -0
- package/src/lab/public/community-files.ts +29 -0
- package/src/lab/public/community.ts +479 -0
- package/src/lab/public/file-safety.ts +155 -0
- package/src/lab/public/ids.ts +26 -0
- package/src/lab/public/index.ts +16 -0
- package/src/lab/public/mutation-lock.ts +424 -0
- package/src/lab/public/operator.ts +353 -0
- package/src/lab/public/origin-purge.ts +79 -0
- package/src/lab/public/origin.ts +203 -0
- package/src/lab/public/privacy.ts +143 -0
- package/src/lab/public/private-file.ts +261 -0
- package/src/lab/public/project.ts +124 -0
- package/src/lab/public/purge-test-fault.ts +21 -0
- package/src/lab/public/purge.ts +223 -0
- package/src/lab/public/registry.ts +44 -0
- package/src/lab/public/revocation.ts +252 -0
- package/src/lab/public/signature.ts +243 -0
- package/src/lab/public/storage.ts +105 -0
- package/src/lab/public/strict-json.ts +206 -0
- package/src/lab/public/time.ts +26 -0
- package/src/lab/public/types.ts +172 -0
- package/src/lab/public/validate.ts +391 -0
- package/src/lab/query/catalog.ts +101 -0
- package/src/lab/query/connection.ts +107 -0
- package/src/lab/query/constants.ts +4 -0
- package/src/lab/query/cursor.ts +132 -0
- package/src/lab/query/dto-map.ts +277 -0
- package/src/lab/query/errors.ts +22 -0
- package/src/lab/query/freshness.ts +53 -0
- package/src/lab/query/index.ts +45 -0
- package/src/lab/query/latest-observation.ts +59 -0
- package/src/lab/query/passive-production.ts +159 -0
- package/src/lab/query/queries.ts +444 -0
- package/src/lab/query/types.ts +266 -0
- package/src/lab/subject/behavior-fingerprint.ts +77 -0
- package/src/lab/subject/installation-salt.ts +112 -0
- package/src/lab/subject/protocol-subject.ts +80 -0
- package/src/lab/subject/route-subject.ts +74 -0
- package/src/lib/app-owned-memory-stores.ts +22 -0
- package/src/lib/bounded-body.ts +153 -9
- package/src/lib/bun-runtime.ts +125 -12
- package/src/lib/bun-stream-caps.ts +13 -9
- package/src/lib/codex-restart-contract.ts +120 -0
- package/src/lib/config-ownership.ts +6 -2
- package/src/lib/destination-policy.ts +65 -1
- package/src/lib/errors.ts +44 -2
- package/src/lib/fabric-task-execution-authority.ts +7 -0
- package/src/lib/fabric-task-host.ts +29 -0
- package/src/lib/lab-activation.ts +223 -0
- package/src/lib/lab-live-execution-authority.ts +13 -0
- package/src/lib/lab-live-host.ts +30 -0
- package/src/lib/lab-live-pinned-sender.ts +56 -0
- package/src/lib/lab-live-route-production.ts +130 -0
- package/src/lib/lab-passive-linker-registration.ts +26 -0
- package/src/lib/local-management-attestation.ts +51 -0
- package/src/lib/local-management-capability.ts +100 -0
- package/src/lib/local-provider-reload-contract.ts +100 -0
- package/src/lib/optional-shutdown-hooks.ts +57 -0
- package/src/lib/pinned-http.ts +145 -26
- package/src/lib/process-control.ts +4 -1
- package/src/lib/provider-outbound.ts +49 -9
- package/src/lib/redact.ts +419 -3
- package/src/lib/self-launch-argv.ts +15 -0
- package/src/lib/server-resource-ownership.ts +71 -0
- package/src/lib/shadow-call.ts +35 -4
- package/src/lib/sse-decoder.ts +41 -0
- package/src/lib/state-store-registrations.ts +10 -2
- package/src/lib/system-restart-contract.ts +73 -0
- package/src/lib/token-estimate.ts +19 -2
- package/src/lib/tool-argument-integers.ts +202 -0
- package/src/lib/translator-budget.ts +44 -0
- package/src/lib/upstream-http-version.ts +57 -0
- package/src/lib/upstream-reachability.ts +95 -0
- package/src/lib/upstream-retry.ts +156 -3
- package/src/lib/windows-atomic-replace.ts +155 -0
- package/src/lib/windows-elevation.ts +70 -2
- package/src/lib/windows-secret-acl.ts +409 -69
- package/src/lib/windows-service-wrappers.ts +72 -0
- package/src/lib/windows-text.ts +106 -0
- package/src/lib/windows-user-principal.ts +341 -0
- package/src/lib/winsw.ts +33 -5
- package/src/oauth/account-import/google-antigravity-adapter.ts +74 -0
- package/src/oauth/account-import/index.ts +15 -0
- package/src/oauth/account-import/parser.ts +83 -0
- package/src/oauth/account-import/registry.ts +18 -0
- package/src/oauth/account-import/service.ts +75 -0
- package/src/oauth/account-import/types.ts +91 -0
- package/src/oauth/anthropic.ts +12 -1
- package/src/oauth/callback-server.ts +8 -2
- package/src/oauth/chatgpt.ts +12 -1
- package/src/oauth/command-code.ts +239 -0
- package/src/oauth/cursor.ts +46 -5
- package/src/oauth/google-antigravity.ts +35 -3
- package/src/oauth/health.ts +20 -12
- package/src/oauth/index.ts +398 -66
- package/src/oauth/key-providers.ts +16 -0
- package/src/oauth/kimi.ts +16 -2
- package/src/oauth/kiro.ts +50 -6
- package/src/oauth/local-token-detect.ts +11 -2
- package/src/oauth/log.ts +3 -1
- package/src/oauth/login-cli.ts +88 -28
- package/src/oauth/nous.ts +798 -0
- package/src/oauth/store.ts +119 -21
- package/src/oauth/token-guardian.ts +9 -3
- package/src/pi/models.ts +2 -2
- package/src/providers/alibaba-region-migration.ts +1 -1
- package/src/providers/antigravity-models.ts +521 -31
- package/src/providers/base-url-choices.ts +10 -0
- package/src/providers/codex-capacity.ts +292 -0
- package/src/providers/command-code-efforts.ts +144 -0
- package/src/providers/context-cap.ts +22 -5
- package/src/providers/cursor-pool.ts +72 -0
- package/src/providers/derive.ts +253 -6
- package/src/providers/fastwire.ts +501 -0
- package/src/providers/free-directory.ts +10 -7
- package/src/providers/google-vertex-location.ts +14 -0
- package/src/providers/key-failover.ts +71 -3
- package/src/providers/label.ts +1 -1
- package/src/providers/model-discovery-limits.ts +16 -0
- package/src/providers/model-discovery.ts +115 -22
- package/src/providers/model-rename-migration.ts +255 -0
- package/src/providers/model-rename-startup.ts +28 -0
- package/src/providers/openai-sidecar.ts +72 -4
- package/src/providers/openai-tier-startup.ts +31 -2
- package/src/providers/openai-tiers.ts +119 -4
- package/src/providers/openai-virtual-models.ts +1 -0
- package/src/providers/opencode-zen-rate-limit.ts +102 -0
- package/src/providers/provider-id-rewrite.ts +29 -0
- package/src/providers/quota.ts +1319 -38
- package/src/providers/registry.ts +1429 -111
- package/src/providers/request-pacing.ts +310 -0
- package/src/providers/service-tier.ts +277 -0
- package/src/providers/slug-codec.ts +42 -6
- package/src/providers/static-model-discovery.ts +86 -0
- package/src/providers/xai-responses-opt-in.ts +15 -0
- package/src/providers/xai-transport.ts +11 -4
- package/src/reasoning-effort.ts +49 -1
- package/src/responses/compaction.ts +26 -1
- package/src/responses/custom-tool-compat.ts +266 -0
- package/src/responses/hosted-tool-policy.ts +9 -0
- package/src/responses/namespace-tool-compat.ts +355 -0
- package/src/responses/parser.ts +220 -38
- package/src/responses/provider-continuation.ts +98 -0
- package/src/responses/provider-opaque-metadata.ts +73 -0
- package/src/responses/reasoning-envelope.ts +9 -1
- package/src/responses/reasoning-replay-cache.ts +426 -0
- package/src/responses/schema.ts +7 -1
- package/src/responses/spill-store.ts +75 -10
- package/src/responses/state.ts +565 -27
- package/src/responses/thought-signature-replay.ts +347 -0
- package/src/responses/tool-search-compat.ts +301 -0
- package/src/responses/truncated-stop-reason.ts +60 -0
- package/src/router.ts +366 -30
- package/src/routing/analytics.ts +378 -0
- package/src/routing/capability.ts +244 -0
- package/src/routing/compatibility/assemble.ts +73 -0
- package/src/routing/compatibility/behavior.ts +278 -0
- package/src/routing/compatibility/catalog.ts +99 -0
- package/src/routing/compatibility/endpoint.ts +52 -0
- package/src/routing/compatibility/lab-evidence-provider.ts +130 -0
- package/src/routing/compatibility/policy.ts +181 -0
- package/src/routing/compatibility/provider-slot.ts +56 -0
- package/src/routing/compatibility/reader.ts +110 -0
- package/src/routing/compatibility/subject.ts +191 -0
- package/src/routing/compatibility/types.ts +64 -0
- package/src/routing/compatibility/version.ts +104 -0
- package/src/routing/cost.ts +77 -0
- package/src/routing/evaluator.ts +495 -0
- package/src/routing/health.ts +412 -0
- package/src/routing/history/cursor.ts +43 -0
- package/src/routing/history/indexer.ts +605 -0
- package/src/routing/history/schema.ts +72 -0
- package/src/routing/profile-namespace.ts +15 -0
- package/src/routing/profile.ts +547 -0
- package/src/routing/quota.ts +145 -0
- package/src/routing/request-evidence.ts +45 -0
- package/src/routing/trace.ts +776 -0
- package/src/server/adapter-resolve.ts +2 -29
- package/src/server/auth-cors.ts +267 -46
- package/src/server/background-lifecycle.ts +182 -0
- package/src/server/chat-completions.ts +130 -56
- package/src/server/chat-native-sse.ts +331 -0
- package/src/server/chat-native.ts +426 -0
- package/src/server/claude-messages.ts +159 -43
- package/src/server/direct-local-http.ts +347 -0
- package/src/server/effort-policy.ts +18 -0
- package/src/server/github-copilot-responses-repair.ts +338 -0
- package/src/server/gui-static.ts +39 -10
- package/src/server/images.ts +94 -12
- package/src/server/index.ts +865 -181
- package/src/server/lifecycle.ts +284 -13
- package/src/server/live.ts +136 -17
- package/src/server/local-management-read-client.ts +90 -0
- package/src/server/local-provider-reload-client.ts +137 -0
- package/src/server/management/agent-settings-routes.ts +398 -103
- package/src/server/management/api-key-usage.ts +31 -5
- package/src/server/management/body.ts +6 -0
- package/src/server/management/combo-routes.ts +62 -24
- package/src/server/management/config-routes.ts +464 -51
- package/src/server/management/context.ts +80 -2
- package/src/server/management/integration-routes.ts +498 -0
- package/src/server/management/lab-automation-routes.ts +206 -0
- package/src/server/management/lab-routes.ts +563 -0
- package/src/server/management/logs-usage-routes.ts +101 -32
- package/src/server/management/model-routes.ts +189 -131
- package/src/server/management/model-rows.ts +163 -0
- package/src/server/management/native-integration-routes.ts +769 -0
- package/src/server/management/oauth-account-routes.ts +80 -4
- package/src/server/management/provider-capability-config.ts +48 -0
- package/src/server/management/provider-routes.ts +764 -157
- package/src/server/management/request-history-routes.ts +191 -0
- package/src/server/management/routing-analytics-routes.ts +74 -0
- package/src/server/management/routing-profile-routes.ts +380 -0
- package/src/server/management/shared.ts +27 -11
- package/src/server/management/sidebar-routes.ts +47 -31
- package/src/server/management/storage-log-guard-routes.ts +186 -0
- package/src/server/management/sync-response.ts +69 -0
- package/src/server/management/system-restart.ts +288 -32
- package/src/server/management/system-routes.ts +77 -0
- package/src/server/management/usage-summary-cache.ts +9 -1
- package/src/server/management/vision-sidecar-options.ts +167 -0
- package/src/server/management/web-search-sidecar-options.ts +120 -0
- package/src/server/management-api.ts +115 -14
- package/src/server/management-auth.ts +220 -5
- package/src/server/passive-route-linker.ts +66 -0
- package/src/server/ports.ts +41 -1
- package/src/server/proxy-liveness.ts +132 -5
- package/src/server/readiness.ts +99 -0
- package/src/server/relay-eager.ts +82 -42
- package/src/server/relay.ts +236 -76
- package/src/server/request-decompress.ts +113 -6
- package/src/server/request-log.ts +235 -22
- package/src/server/responses/agent-task-recovery-cache.ts +143 -0
- package/src/server/responses/agent-task-recovery.ts +465 -0
- package/src/server/responses/collaboration.ts +204 -35
- package/src/server/responses/compact.ts +442 -55
- package/src/server/responses/core.ts +2872 -331
- package/src/server/responses/empty-completion-guard.ts +276 -0
- package/src/server/responses/encrypted-payload.ts +62 -39
- package/src/server/responses/fetch-helpers.ts +79 -4
- package/src/server/responses/input-admission.ts +185 -0
- package/src/server/responses/pacing-overload.ts +13 -0
- package/src/server/responses/policy-fallback.ts +178 -0
- package/src/server/responses/responses-field-backfill.ts +251 -0
- package/src/server/responses/terminal-guard.ts +26 -5
- package/src/server/responses/upstream-error.ts +5 -0
- package/src/server/responses/ws-upstream.ts +308 -0
- package/src/server/responses-custom-tool-repair.ts +282 -0
- package/src/server/responses-item-id-repair.ts +54 -6
- package/src/server/responses-json-events.ts +90 -0
- package/src/server/responses-model-rewrite.ts +29 -0
- package/src/server/responses-reasoning-summary-rewrite.ts +178 -0
- package/src/server/responses-snapshot-repair.ts +621 -0
- package/src/server/responses-terminal-repair.ts +342 -0
- package/src/server/responses-tool-search-repair.ts +267 -0
- package/src/server/responses-undeclared-tool-guard.ts +153 -0
- package/src/server/responses.ts +18 -2
- package/src/server/search.ts +78 -13
- package/src/server/sse-frame-buffer.ts +292 -0
- package/src/server/sse-payload-rewrite.ts +110 -22
- package/src/server/startup-action-control.ts +8 -1
- package/src/server/startup-health-cache.ts +19 -1
- package/src/server/system-env.ts +80 -9
- package/src/server/ws-bridge.ts +39 -38
- package/src/service-manager-probe.ts +892 -0
- package/src/service.ts +1111 -90
- package/src/sidecar/auth.ts +92 -0
- package/src/sidecar/candidates.ts +83 -0
- package/src/storage/cleanup.ts +2 -2
- package/src/storage/scanner.ts +1 -1
- package/src/storage/worker-lifecycle.ts +14 -14
- package/src/tray/windows-tray.ps1 +83 -9
- package/src/tray/windows.ts +43 -16
- package/src/types/accounts.ts +37 -0
- package/src/types/config.ts +845 -0
- package/src/types/provider.ts +545 -0
- package/src/types/request.ts +384 -0
- package/src/types/tools.ts +131 -0
- package/src/types/wire.ts +80 -0
- package/src/types.ts +104 -1236
- package/src/update/index.ts +32 -19
- package/src/update/job.ts +442 -67
- package/src/update/notify.ts +12 -6
- package/src/update/npm-cache-preflight.d.mts +47 -0
- package/src/update/npm-cache-preflight.mjs +201 -0
- package/src/update/transactional-install.d.mts +22 -0
- package/src/update/transactional-install.mjs +259 -0
- package/src/usage/cost.ts +0 -0
- package/src/usage/expected-prices.ts +268 -16
- package/src/usage/log.ts +606 -41
- package/src/usage/summary.ts +177 -9
- package/src/usage/user-cost-overlay-reconciler.ts +313 -0
- package/src/usage/user-cost-overlays.ts +314 -0
- package/src/vision/anthropic-describe.ts +10 -6
- package/src/vision/backends.ts +97 -0
- package/src/vision/describe.ts +9 -3
- package/src/vision/eligibility.ts +250 -0
- package/src/vision/index.ts +238 -24
- package/src/vision/reasoning.ts +55 -0
- package/src/vision/routed-describe.ts +175 -0
- package/src/vision/timeout-bounds.ts +9 -0
- package/src/web-search/anthropic-executor.ts +13 -7
- package/src/web-search/backends.ts +108 -0
- package/src/web-search/exa-executor.ts +88 -0
- package/src/web-search/executor.ts +11 -3
- package/src/web-search/gemini-executor.ts +141 -0
- package/src/web-search/index.ts +150 -15
- package/src/web-search/loop.ts +279 -50
- package/src/web-search/parse.ts +125 -30
- package/src/web-search/sources.ts +60 -0
- package/src/web-search/xai-executor.ts +219 -0
- package/gui/dist/assets/index-DTpMHS4F.js +0 -67
- package/gui/dist/assets/index-ZNVDE3C7.css +0 -1
- package/gui/dist/provider-icons/antigravity.svg +0 -1
- package/gui/dist/provider-icons/claude.svg +0 -1
- package/gui/dist/provider-icons/copilot.svg +0 -1
- package/gui/dist/provider-icons/cursor.svg +0 -2
- package/gui/dist/provider-icons/gemini.svg +0 -1
- package/gui/dist/provider-icons/grok-color.svg +0 -1
- package/gui/dist/provider-icons/kiro.svg +0 -14
- package/src/cli/internal-dispatch.ts +0 -20
|
@@ -0,0 +1,426 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-process fallback store pairing raw reasoning text with the tool call it
|
|
3
|
+
* preceded (issue #950).
|
|
4
|
+
*
|
|
5
|
+
* DeepSeek thinking mode requires the assistant's original `reasoning_content`
|
|
6
|
+
* to be replayed on every continuation of a tool-call turn. The bridge records
|
|
7
|
+
* the raw reasoning here when it closes a reasoning block and a tool call
|
|
8
|
+
* follows; the openai-chat adapter re-attaches it when a `tool_calls`
|
|
9
|
+
* assistant message is about to serialize without thinking parts (compacted
|
|
10
|
+
* history, lost assistant turn, orphan-repaired tool results).
|
|
11
|
+
*
|
|
12
|
+
* Entries require a conversation identity in addition to the call id:
|
|
13
|
+
* provider-generated ids like `call_1` are not globally unique, so an
|
|
14
|
+
* unscoped process-wide key would let one conversation's reasoning bleed into
|
|
15
|
+
* another when ids collide (CodeRabbit P1 on #971).
|
|
16
|
+
*
|
|
17
|
+
* Privacy: entries hold reasoning text in memory only — never logged,
|
|
18
|
+
* serialized, or exported. Bounded by entry count, total bytes, and TTL, so a
|
|
19
|
+
* long-lived proxy cannot grow without limit.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { createHash, createHmac, randomBytes } from "node:crypto";
|
|
23
|
+
import type {
|
|
24
|
+
OcxProviderConfig,
|
|
25
|
+
OcxReasoningReplayIdentity,
|
|
26
|
+
OcxReasoningReplayScopeRef,
|
|
27
|
+
} from "../types";
|
|
28
|
+
|
|
29
|
+
const MAX_ENTRIES = 64;
|
|
30
|
+
const MAX_TOTAL_BYTES = 256 * 1024;
|
|
31
|
+
const TTL_MS = 60 * 60 * 1000;
|
|
32
|
+
const replayIdentityKey = randomBytes(32);
|
|
33
|
+
const CREDENTIAL_HEADER_NAMES = new Set([
|
|
34
|
+
"authorization",
|
|
35
|
+
"api-key",
|
|
36
|
+
"chatgpt-account-id",
|
|
37
|
+
"cookie",
|
|
38
|
+
"openai-organization",
|
|
39
|
+
"openai-project",
|
|
40
|
+
"proxy-authorization",
|
|
41
|
+
"x-api-key",
|
|
42
|
+
"x-api-token",
|
|
43
|
+
"x-auth-token",
|
|
44
|
+
"x-goog-api-key",
|
|
45
|
+
"x-openai-organization",
|
|
46
|
+
"x-openai-project",
|
|
47
|
+
]);
|
|
48
|
+
|
|
49
|
+
interface CacheEntry {
|
|
50
|
+
text: string;
|
|
51
|
+
bytes: number;
|
|
52
|
+
at: number;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
interface ServingIdentityEntry {
|
|
56
|
+
identity: string;
|
|
57
|
+
bytes: number;
|
|
58
|
+
at: number;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const entries = new Map<string, CacheEntry>();
|
|
62
|
+
const servingIdentities = new Map<string, ServingIdentityEntry>();
|
|
63
|
+
let totalBytes = 0;
|
|
64
|
+
let servingIdentityTotalBytes = 0;
|
|
65
|
+
let clockForTests: (() => number) | null = null;
|
|
66
|
+
|
|
67
|
+
const now = (): number => clockForTests?.() ?? Date.now();
|
|
68
|
+
|
|
69
|
+
function nonEmpty(value: unknown): value is string {
|
|
70
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
type ReasoningReplayIdentityTuple = readonly [string, string, string, string, string];
|
|
74
|
+
|
|
75
|
+
function tupleForIdentity(
|
|
76
|
+
identity: Readonly<OcxReasoningReplayIdentity> | undefined,
|
|
77
|
+
): ReasoningReplayIdentityTuple | undefined {
|
|
78
|
+
if (
|
|
79
|
+
!nonEmpty(identity?.providerName)
|
|
80
|
+
|| !nonEmpty(identity?.providerDestinationIdentity)
|
|
81
|
+
|| !nonEmpty(identity?.adapterName)
|
|
82
|
+
|| !nonEmpty(identity?.modelId)
|
|
83
|
+
|| !nonEmpty(identity?.credentialIdentity)
|
|
84
|
+
) return undefined;
|
|
85
|
+
return [
|
|
86
|
+
identity.providerName,
|
|
87
|
+
identity.providerDestinationIdentity,
|
|
88
|
+
identity.adapterName,
|
|
89
|
+
identity.modelId,
|
|
90
|
+
identity.credentialIdentity,
|
|
91
|
+
];
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function tupleForServingIdentity(
|
|
95
|
+
identity: Readonly<OcxReasoningReplayIdentity> | undefined,
|
|
96
|
+
): ReasoningReplayIdentityTuple | undefined {
|
|
97
|
+
if (
|
|
98
|
+
!nonEmpty(identity?.providerName)
|
|
99
|
+
|| !nonEmpty(identity?.providerDestinationDurableIdentity)
|
|
100
|
+
|| !nonEmpty(identity?.adapterName)
|
|
101
|
+
|| !nonEmpty(identity?.modelId)
|
|
102
|
+
|| !nonEmpty(identity?.credentialDurableIdentity)
|
|
103
|
+
) return undefined;
|
|
104
|
+
return [
|
|
105
|
+
identity.providerName,
|
|
106
|
+
identity.providerDestinationDurableIdentity,
|
|
107
|
+
identity.adapterName,
|
|
108
|
+
identity.modelId,
|
|
109
|
+
identity.credentialDurableIdentity,
|
|
110
|
+
];
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function keyFor(callId: string, scope: OcxReasoningReplayScopeRef | undefined): string | undefined {
|
|
114
|
+
const identity = tupleForIdentity(scope?.current);
|
|
115
|
+
if (!nonEmpty(callId) || !nonEmpty(scope?.clientThreadId) || !identity) return undefined;
|
|
116
|
+
return JSON.stringify([
|
|
117
|
+
scope.clientThreadId,
|
|
118
|
+
...identity,
|
|
119
|
+
callId,
|
|
120
|
+
]);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function deleteServingIdentity(threadId: string): void {
|
|
124
|
+
const entry = servingIdentities.get(threadId);
|
|
125
|
+
if (!entry) return;
|
|
126
|
+
servingIdentities.delete(threadId);
|
|
127
|
+
servingIdentityTotalBytes -= entry.bytes;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function sweepExpiredServingIdentities(at: number): void {
|
|
131
|
+
for (const [threadId, entry] of servingIdentities) {
|
|
132
|
+
if (at - entry.at >= TTL_MS) deleteServingIdentity(threadId);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function servingIdentityFor(
|
|
137
|
+
scope: OcxReasoningReplayScopeRef | undefined,
|
|
138
|
+
): { threadId: string; identity: string } | undefined {
|
|
139
|
+
const threadId = scope?.clientThreadId;
|
|
140
|
+
const identityTuple = tupleForServingIdentity(scope?.current);
|
|
141
|
+
if (!nonEmpty(threadId) || !identityTuple) return undefined;
|
|
142
|
+
return { threadId, identity: JSON.stringify(identityTuple) };
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Compare this request's route with the last successfully serving route for its client thread.
|
|
147
|
+
* A live mismatch means replayed opaque reasoning was minted by another backend and must not be
|
|
148
|
+
* forwarded to this one. Comparison deliberately does not refresh or replace the recorded route:
|
|
149
|
+
* a failed candidate request did not serve the thread.
|
|
150
|
+
*
|
|
151
|
+
* Serving provenance uses restart-stable destination and credential dimensions so token
|
|
152
|
+
* generations and other volatile credential material cannot create false route changes. Missing
|
|
153
|
+
* durable identity, expired, or evicted state is deliberately unknown rather than a mismatch.
|
|
154
|
+
* This store is process-local, so a backend switch spanning a proxy restart is not detected.
|
|
155
|
+
*/
|
|
156
|
+
export function reasoningReplayServingIdentityChanged(
|
|
157
|
+
scope: OcxReasoningReplayScopeRef | undefined,
|
|
158
|
+
): boolean {
|
|
159
|
+
const current = servingIdentityFor(scope);
|
|
160
|
+
if (!current) return false;
|
|
161
|
+
const at = now();
|
|
162
|
+
sweepExpiredServingIdentities(at);
|
|
163
|
+
const previous = servingIdentities.get(current.threadId);
|
|
164
|
+
return previous !== undefined && previous.identity !== current.identity;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/** Record the route only after it has successfully served the client thread. */
|
|
168
|
+
export function commitReasoningReplayServingIdentity(
|
|
169
|
+
scope: OcxReasoningReplayScopeRef | undefined,
|
|
170
|
+
): void {
|
|
171
|
+
const current = servingIdentityFor(scope);
|
|
172
|
+
if (!current) return;
|
|
173
|
+
const at = now();
|
|
174
|
+
sweepExpiredServingIdentities(at);
|
|
175
|
+
const previous = servingIdentities.get(current.threadId);
|
|
176
|
+
const { threadId, identity } = current;
|
|
177
|
+
const bytes = Buffer.byteLength(JSON.stringify([threadId, identity]), "utf8");
|
|
178
|
+
if (bytes > MAX_TOTAL_BYTES) {
|
|
179
|
+
deleteServingIdentity(threadId);
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (previous) deleteServingIdentity(threadId);
|
|
184
|
+
servingIdentities.set(threadId, { identity, bytes, at });
|
|
185
|
+
servingIdentityTotalBytes += bytes;
|
|
186
|
+
while (
|
|
187
|
+
(servingIdentityTotalBytes > MAX_TOTAL_BYTES || servingIdentities.size > MAX_ENTRIES)
|
|
188
|
+
&& servingIdentities.size > 1
|
|
189
|
+
) {
|
|
190
|
+
let oldestThreadId: string | undefined;
|
|
191
|
+
let oldestAt = Infinity;
|
|
192
|
+
for (const [candidateThreadId, entry] of servingIdentities) {
|
|
193
|
+
if (entry.at < oldestAt) {
|
|
194
|
+
oldestAt = entry.at;
|
|
195
|
+
oldestThreadId = candidateThreadId;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
if (oldestThreadId === undefined) break;
|
|
199
|
+
deleteServingIdentity(oldestThreadId);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function processLocalIdentity(domain: string, material: string): string {
|
|
204
|
+
return createHmac("sha256", replayIdentityKey)
|
|
205
|
+
.update(domain)
|
|
206
|
+
.update("\0")
|
|
207
|
+
.update(material)
|
|
208
|
+
.digest("hex");
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function credentialHeaderOverrides(headers: Record<string, string> | undefined): [string, string][] {
|
|
212
|
+
return Object.entries(headers ?? {})
|
|
213
|
+
.filter(([name, value]) => CREDENTIAL_HEADER_NAMES.has(name.trim().toLowerCase()) && nonEmpty(value))
|
|
214
|
+
.map(([name, value]) => [name.trim().toLowerCase(), value] as [string, string])
|
|
215
|
+
.sort(([leftName, leftValue], [rightName, rightValue]) => (
|
|
216
|
+
leftName.localeCompare(rightName) || leftValue.localeCompare(rightValue)
|
|
217
|
+
));
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/** Produce a non-reversible process-local identity for an exact upstream destination. */
|
|
221
|
+
export function reasoningReplayDestinationIdentity(baseUrl: string | undefined): string | undefined {
|
|
222
|
+
if (!nonEmpty(baseUrl)) return undefined;
|
|
223
|
+
const canonical = baseUrl.trim().replace(/\/+$/, "");
|
|
224
|
+
return `destination:${processLocalIdentity("destination", canonical)}`;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* The same destination identity, but stable across restarts.
|
|
229
|
+
*
|
|
230
|
+
* The process-local form above is keyed by `randomBytes(32)` minted at module load, which
|
|
231
|
+
* is correct for an in-memory cache and fatal for a durable one: every key would change on
|
|
232
|
+
* restart and the store would silently stop matching anything. A plain digest of the same
|
|
233
|
+
* canonical URL is equally non-reversible for this purpose — the input is a configured
|
|
234
|
+
* endpoint, not a secret — and needs no persisted salt or new on-disk state.
|
|
235
|
+
*/
|
|
236
|
+
export function durableReplayDestinationIdentity(baseUrl: string | undefined): string | undefined {
|
|
237
|
+
if (!nonEmpty(baseUrl)) return undefined;
|
|
238
|
+
const canonical = baseUrl.trim().replace(/\/+$/, "");
|
|
239
|
+
return `destination:${createHash("sha256").update("destination\0").update(canonical).digest("hex")}`;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Restart-stable credential identity for the DURABLE thought-signature store (#1926).
|
|
244
|
+
*
|
|
245
|
+
* Unlike the destination, credential material may be secret (an API key), so a plain
|
|
246
|
+
* unsalted digest would turn the store file into an offline verifier for candidate keys.
|
|
247
|
+
* The identity is therefore an HMAC under a random salt persisted NEXT TO the store: the
|
|
248
|
+
* salt is not a secret escrow (it holds no credential material) but it makes every digest
|
|
249
|
+
* useless outside this installation. Full 256-bit output — no truncation.
|
|
250
|
+
*
|
|
251
|
+
* OAuth accounts use the persisted account-slot id (not the rotating token/generation):
|
|
252
|
+
* relinking a slot to a different upstream account keeps the id, but the upstream then
|
|
253
|
+
* validates signatures against the new credential and rejects stale ones — the same
|
|
254
|
+
* fail-closed backstop the destination identity relies on. Credential-scoped header
|
|
255
|
+
* overrides participate so two provider entries sharing one key but different
|
|
256
|
+
* authorization headers stay distinct, mirroring the process-local identity.
|
|
257
|
+
*/
|
|
258
|
+
export function durableReplayCredentialIdentity(
|
|
259
|
+
kind: "key" | "oauth" | "codex",
|
|
260
|
+
material: string | undefined,
|
|
261
|
+
headers: Record<string, string> | undefined,
|
|
262
|
+
salt: Buffer | undefined,
|
|
263
|
+
): string | undefined {
|
|
264
|
+
if (!nonEmpty(material) || !salt || salt.length < 16) return undefined;
|
|
265
|
+
const overrides = credentialHeaderOverrides(headers);
|
|
266
|
+
return `credential:${createHmac("sha256", salt)
|
|
267
|
+
.update(`credential\0${kind}\0`)
|
|
268
|
+
.update(JSON.stringify([material, overrides]))
|
|
269
|
+
.digest("hex")}`;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/** Produce a non-reversible process-local identity for credential material. */
|
|
273
|
+
export function reasoningReplayCredentialIdentity(
|
|
274
|
+
kind: "key" | "oauth" | "codex",
|
|
275
|
+
material: string | undefined,
|
|
276
|
+
headers?: Record<string, string>,
|
|
277
|
+
): string | undefined {
|
|
278
|
+
if (!nonEmpty(material)) return undefined;
|
|
279
|
+
const overrides = credentialHeaderOverrides(headers);
|
|
280
|
+
return `${kind}:${processLocalIdentity(`credential:${kind}`, JSON.stringify([material, overrides]))}`;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/** Bind Codex forwarding to the effective bearer/account and selected physical pool slot. */
|
|
284
|
+
export function reasoningReplayCodexCredentialIdentity(args: {
|
|
285
|
+
authorization?: string | null;
|
|
286
|
+
chatgptAccountId?: string | null;
|
|
287
|
+
accountId?: string | null;
|
|
288
|
+
credentialGeneration?: number | string | null;
|
|
289
|
+
writerGeneration?: number | string | null;
|
|
290
|
+
headers?: Record<string, string>;
|
|
291
|
+
}): string | undefined {
|
|
292
|
+
const configuredAuthorization = credentialHeaderOverrides(args.headers)
|
|
293
|
+
.find(([name]) => name === "authorization")?.[1];
|
|
294
|
+
const authorization = nonEmpty(args.authorization) ? args.authorization : configuredAuthorization;
|
|
295
|
+
if (!authorization) return undefined;
|
|
296
|
+
const material = JSON.stringify([
|
|
297
|
+
authorization,
|
|
298
|
+
nonEmpty(args.chatgptAccountId) ? args.chatgptAccountId : "",
|
|
299
|
+
nonEmpty(args.accountId) ? args.accountId : "",
|
|
300
|
+
args.credentialGeneration === null || args.credentialGeneration === undefined
|
|
301
|
+
? ""
|
|
302
|
+
: String(args.credentialGeneration),
|
|
303
|
+
args.writerGeneration === null || args.writerGeneration === undefined
|
|
304
|
+
? ""
|
|
305
|
+
: String(args.writerGeneration),
|
|
306
|
+
]);
|
|
307
|
+
return reasoningReplayCredentialIdentity("codex", material, args.headers);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/** Bind OAuth replay to one persisted credential slot and exact token generation. */
|
|
311
|
+
export function reasoningReplayOAuthCredentialIdentity(
|
|
312
|
+
snapshot: Readonly<{ accountId: string; generation: string }> | undefined,
|
|
313
|
+
headers?: Record<string, string>,
|
|
314
|
+
): string | undefined {
|
|
315
|
+
if (!snapshot || !nonEmpty(snapshot.accountId) || !nonEmpty(snapshot.generation)) return undefined;
|
|
316
|
+
return reasoningReplayCredentialIdentity(
|
|
317
|
+
"oauth",
|
|
318
|
+
`${snapshot.accountId}\0${snapshot.generation}`,
|
|
319
|
+
headers,
|
|
320
|
+
);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/** Bind key-auth provider material without putting raw secrets in the replay key. */
|
|
324
|
+
export function reasoningReplayKeyCredentialIdentity(
|
|
325
|
+
provider: Pick<OcxProviderConfig, "apiKey" | "headers">,
|
|
326
|
+
): string | undefined {
|
|
327
|
+
const apiKey = nonEmpty(provider.apiKey) ? provider.apiKey : undefined;
|
|
328
|
+
// Public/static headers do not establish a physical credential boundary.
|
|
329
|
+
// Header-only/key-optional providers therefore fail closed for replay.
|
|
330
|
+
if (!apiKey) return undefined;
|
|
331
|
+
return reasoningReplayCredentialIdentity("key", apiKey, provider.headers);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/** Replace only the holder snapshot so parsed-request copies observe rotations. */
|
|
335
|
+
export function bindReasoningReplayScope(
|
|
336
|
+
scope: OcxReasoningReplayScopeRef | undefined,
|
|
337
|
+
identity: OcxReasoningReplayIdentity | undefined,
|
|
338
|
+
): void {
|
|
339
|
+
if (!scope) return;
|
|
340
|
+
if (!identity || !keyFor("binding-check", { ...scope, current: identity })) {
|
|
341
|
+
delete scope.current;
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
scope.current = { ...identity };
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Record the raw reasoning text that preceded the given tool call.
|
|
349
|
+
*
|
|
350
|
+
* Expired entries are swept on insert so the TTL bound holds even when a call
|
|
351
|
+
* id is never read again.
|
|
352
|
+
*/
|
|
353
|
+
export function rememberReasoningForCall(
|
|
354
|
+
callId: string,
|
|
355
|
+
text: string,
|
|
356
|
+
scope?: OcxReasoningReplayScopeRef,
|
|
357
|
+
): void {
|
|
358
|
+
// Never fall back to a process-wide namespace. Call ids are supplied by
|
|
359
|
+
// clients/providers and are therefore neither unique nor trustworthy; an
|
|
360
|
+
// unscoped entry could be recovered by an unrelated request that reuses the
|
|
361
|
+
// same id.
|
|
362
|
+
// Empty provider deltas are absence of new reasoning, not a request to erase a candidate.
|
|
363
|
+
const key = keyFor(callId, scope);
|
|
364
|
+
if (!key || typeof text !== "string" || text.length === 0) return;
|
|
365
|
+
const bytes = Buffer.byteLength(text, "utf8");
|
|
366
|
+
// A single entry larger than the whole budget would immediately evict itself.
|
|
367
|
+
if (bytes > MAX_TOTAL_BYTES) return;
|
|
368
|
+
const at = now();
|
|
369
|
+
// Delete every due entry first so expired reasoning cannot linger until a
|
|
370
|
+
// later peek or capacity eviction.
|
|
371
|
+
for (const [key, entry] of entries) {
|
|
372
|
+
if (at - entry.at >= TTL_MS) {
|
|
373
|
+
entries.delete(key);
|
|
374
|
+
totalBytes -= entry.bytes;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
const previous = entries.get(key);
|
|
378
|
+
if (previous) totalBytes -= previous.bytes;
|
|
379
|
+
entries.set(key, { text, bytes, at });
|
|
380
|
+
totalBytes += bytes;
|
|
381
|
+
// Evict oldest-first until both caps hold (never evict the entry just written
|
|
382
|
+
// while it is the only one — the loop guards on size > 1).
|
|
383
|
+
while ((totalBytes > MAX_TOTAL_BYTES || entries.size > MAX_ENTRIES) && entries.size > 1) {
|
|
384
|
+
let oldestKey: string | undefined;
|
|
385
|
+
let oldestAt = Infinity;
|
|
386
|
+
for (const [candidateKey, entry] of entries) {
|
|
387
|
+
if (entry.at < oldestAt) {
|
|
388
|
+
oldestAt = entry.at;
|
|
389
|
+
oldestKey = candidateKey;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
if (oldestKey === undefined) break;
|
|
393
|
+
const evicted = entries.get(oldestKey)!;
|
|
394
|
+
totalBytes -= evicted.bytes;
|
|
395
|
+
entries.delete(oldestKey);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Read the recorded reasoning for a call id without removing it: retries after
|
|
401
|
+
* a failed continuation reuse the same fallback.
|
|
402
|
+
*/
|
|
403
|
+
export function peekReasoningForCall(
|
|
404
|
+
callId: string,
|
|
405
|
+
scope?: OcxReasoningReplayScopeRef,
|
|
406
|
+
): string | undefined {
|
|
407
|
+
const key = keyFor(callId, scope);
|
|
408
|
+
if (!key) return undefined;
|
|
409
|
+
const entry = entries.get(key);
|
|
410
|
+
if (!entry) return undefined;
|
|
411
|
+
if (now() - entry.at >= TTL_MS) {
|
|
412
|
+
entries.delete(key);
|
|
413
|
+
totalBytes -= entry.bytes;
|
|
414
|
+
return undefined;
|
|
415
|
+
}
|
|
416
|
+
return entry.text;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/** Test-only: reset the cache and optionally pin the clock. */
|
|
420
|
+
export function clearReasoningReplayCacheForTests(clock?: (() => number) | null): void {
|
|
421
|
+
entries.clear();
|
|
422
|
+
servingIdentities.clear();
|
|
423
|
+
totalBytes = 0;
|
|
424
|
+
servingIdentityTotalBytes = 0;
|
|
425
|
+
clockForTests = clock ?? null;
|
|
426
|
+
}
|
package/src/responses/schema.ts
CHANGED
|
@@ -64,6 +64,12 @@ const functionCallItemSchema = z.object({
|
|
|
64
64
|
name: z.string().min(1),
|
|
65
65
|
namespace: z.string().optional(),
|
|
66
66
|
arguments: z.string().optional(),
|
|
67
|
+
// Provider-opaque metadata that must survive the round trip verbatim (issue #1735). The shape
|
|
68
|
+
// is bounded on purpose: only the one nested key we round-trip is modeled, so an unexpected
|
|
69
|
+
// payload cannot ride through as arbitrary passthrough state.
|
|
70
|
+
extra_content: z.object({
|
|
71
|
+
google: z.object({ thought_signature: z.string().optional() }).optional(),
|
|
72
|
+
}).optional(),
|
|
67
73
|
});
|
|
68
74
|
const functionCallOutputItemSchema = z.object({
|
|
69
75
|
type: z.literal("function_call_output"),
|
|
@@ -107,7 +113,7 @@ export const toolSchema = z.object({
|
|
|
107
113
|
const builtinToolSchema = z.object({ type: z.string() }).loose();
|
|
108
114
|
|
|
109
115
|
const hostedToolType = z.enum([
|
|
110
|
-
"web_search_preview", "file_search", "computer_use_preview",
|
|
116
|
+
"web_search", "web_search_preview", "file_search", "computer_use_preview",
|
|
111
117
|
"code_interpreter", "image_generation", "mcp",
|
|
112
118
|
]);
|
|
113
119
|
|
|
@@ -17,7 +17,8 @@ import {
|
|
|
17
17
|
import { createHash, randomBytes } from "node:crypto";
|
|
18
18
|
import { join } from "node:path";
|
|
19
19
|
import { getConfigDir } from "../config";
|
|
20
|
-
import { forgetHardenedSecretPath, hardenSecretDir, hardenSecretPath } from "../lib/windows-secret-acl";
|
|
20
|
+
import { forgetEphemeralSecretPath, forgetHardenedSecretPath, hardenSecretDir, hardenSecretPath } from "../lib/windows-secret-acl";
|
|
21
|
+
import { isValidProviderContinuationOwner } from "./provider-continuation";
|
|
21
22
|
import type { OcxProviderContinuationState } from "../types";
|
|
22
23
|
|
|
23
24
|
export const RESPONSE_SPILL_VERSION = 1;
|
|
@@ -34,7 +35,19 @@ export interface ResponseSpillPayload {
|
|
|
34
35
|
version: 1;
|
|
35
36
|
responseId: string;
|
|
36
37
|
createdAt: number;
|
|
38
|
+
clientThreadId?: string;
|
|
37
39
|
items: unknown[];
|
|
40
|
+
/**
|
|
41
|
+
* Index in `items` where the provider output begins, used by replay-overlap detection
|
|
42
|
+
* in state.ts. Optional so a payload written before this field still loads (it simply
|
|
43
|
+
* never authorizes a skip).
|
|
44
|
+
*
|
|
45
|
+
* Compatibility is FORWARD-ONLY: `validPayload` is a strict key allowlist, so a build
|
|
46
|
+
* predating this field rejects a payload carrying it as corrupt rather than ignoring
|
|
47
|
+
* it. Rolling back across this change invalidates spilled entries, which degrades to a
|
|
48
|
+
* replay miss — an already-handled path — not to corrupted live state.
|
|
49
|
+
*/
|
|
50
|
+
providerOutputStart?: number;
|
|
38
51
|
providers?: OcxProviderContinuationState;
|
|
39
52
|
}
|
|
40
53
|
|
|
@@ -45,9 +58,29 @@ export interface ResponseSpillRef {
|
|
|
45
58
|
payloadBytes: number;
|
|
46
59
|
}
|
|
47
60
|
|
|
61
|
+
/**
|
|
62
|
+
* Hard ceiling for one spill payload, enforced BOTH at direct-spill admission
|
|
63
|
+
* (state.ts refuses to durably retain a larger candidate) and at replay read
|
|
64
|
+
* (below). 256 MiB keeps the replay transient under the process-wide
|
|
65
|
+
* APP_OWNED_WORST_CASE_PINNED_BYTES ceiling (512 MiB); without an admission
|
|
66
|
+
* ceiling the read ceiling would strand write-only spills on disk.
|
|
67
|
+
*/
|
|
68
|
+
export const MAX_RESPONSE_SPILL_PAYLOAD_BYTES = 256 * 1024 * 1024;
|
|
69
|
+
|
|
70
|
+
let spillPayloadCapOverride: number | null = null;
|
|
71
|
+
|
|
72
|
+
/** Test-only: lower/restore the single-spill payload ceiling (null restores). */
|
|
73
|
+
export function setResponseSpillPayloadCapForTests(bytes: number | null): void {
|
|
74
|
+
spillPayloadCapOverride = bytes;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function responseSpillPayloadCap(): number {
|
|
78
|
+
return spillPayloadCapOverride ?? MAX_RESPONSE_SPILL_PAYLOAD_BYTES;
|
|
79
|
+
}
|
|
80
|
+
|
|
48
81
|
export type ResponseSpillReadResult =
|
|
49
82
|
| { ok: true; payload: ResponseSpillPayload }
|
|
50
|
-
| { ok: false; reason: "missing" | "corrupt" };
|
|
83
|
+
| { ok: false; reason: "missing" | "corrupt" | "too_large" };
|
|
51
84
|
|
|
52
85
|
export interface ResponseSpillCleanupResult {
|
|
53
86
|
scanned: number;
|
|
@@ -176,17 +209,28 @@ function closeFile(fd: number): void {
|
|
|
176
209
|
record("close");
|
|
177
210
|
}
|
|
178
211
|
|
|
179
|
-
function unlink(path: string): void {
|
|
212
|
+
function unlink(path: string, ephemeral = false): void {
|
|
180
213
|
try {
|
|
181
214
|
if (spillIoForTest?.unlink) spillIoForTest.unlink(path);
|
|
182
215
|
else unlinkSync(path);
|
|
183
|
-
|
|
216
|
+
// Ephemeral release only for publish temps; stable spill files keep their
|
|
217
|
+
// destination-keyed timeout memos (anti-restall) and drop just the
|
|
218
|
+
// success memo for the now-deleted file.
|
|
219
|
+
if (ephemeral) forgetEphemeralSecretPath(path);
|
|
220
|
+
else forgetHardenedSecretPath(path);
|
|
184
221
|
} catch (error) {
|
|
185
|
-
if (isErrno(error, "ENOENT"))
|
|
222
|
+
if (isErrno(error, "ENOENT")) {
|
|
223
|
+
if (ephemeral) forgetEphemeralSecretPath(path);
|
|
224
|
+
else forgetHardenedSecretPath(path);
|
|
225
|
+
}
|
|
186
226
|
throw error;
|
|
187
227
|
}
|
|
188
228
|
}
|
|
189
229
|
|
|
230
|
+
function unlinkEphemeral(path: string): void {
|
|
231
|
+
unlink(path, true);
|
|
232
|
+
}
|
|
233
|
+
|
|
190
234
|
function publishNoReplace(tempPath: string, destinationPath: string): void {
|
|
191
235
|
try {
|
|
192
236
|
if (spillIoForTest?.link) spillIoForTest.link(tempPath, destinationPath);
|
|
@@ -229,13 +273,26 @@ function validPayload(value: unknown, responseId: string): value is ResponseSpil
|
|
|
229
273
|
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
230
274
|
const payload = value as Record<string, unknown>;
|
|
231
275
|
const keys = Object.keys(payload);
|
|
232
|
-
if (keys.some(key => !["version", "responseId", "createdAt", "items", "providers"].includes(key))) return false;
|
|
276
|
+
if (keys.some(key => !["version", "responseId", "createdAt", "clientThreadId", "items", "providerOutputStart", "providers"].includes(key))) return false;
|
|
233
277
|
if (payload.version !== 1 || payload.responseId !== responseId) return false;
|
|
234
278
|
if (typeof payload.createdAt !== "number" || !Number.isFinite(payload.createdAt)) return false;
|
|
279
|
+
if (payload.clientThreadId !== undefined
|
|
280
|
+
&& (typeof payload.clientThreadId !== "string" || payload.clientThreadId.trim().length === 0)) return false;
|
|
235
281
|
if (!Array.isArray(payload.items)) return false;
|
|
282
|
+
// A malformed boundary must degrade to "never skip", never to a bad index: reject the
|
|
283
|
+
// payload outright so materialization treats it as corrupt rather than trusting it.
|
|
284
|
+
if (payload.providerOutputStart !== undefined) {
|
|
285
|
+
const anchor = payload.providerOutputStart;
|
|
286
|
+
if (typeof anchor !== "number" || !Number.isSafeInteger(anchor)
|
|
287
|
+
|| anchor < 0 || anchor > payload.items.length) return false;
|
|
288
|
+
}
|
|
236
289
|
if (payload.providers !== undefined) {
|
|
237
290
|
if (!payload.providers || typeof payload.providers !== "object" || Array.isArray(payload.providers)) return false;
|
|
238
|
-
|
|
291
|
+
const providers = payload.providers as Record<string, unknown>;
|
|
292
|
+
if (providers.__ocxOwner !== undefined
|
|
293
|
+
&& !isValidProviderContinuationOwner(providers.__ocxOwner)) return false;
|
|
294
|
+
for (const [provider, providerState] of Object.entries(providers)) {
|
|
295
|
+
if (provider === "__ocxOwner") continue;
|
|
239
296
|
if (!providerState || typeof providerState !== "object" || Array.isArray(providerState)) return false;
|
|
240
297
|
}
|
|
241
298
|
}
|
|
@@ -253,7 +310,9 @@ export function writeResponseSpillDurably(
|
|
|
253
310
|
version: 1,
|
|
254
311
|
responseId,
|
|
255
312
|
createdAt: state.createdAt,
|
|
313
|
+
...(state.clientThreadId ? { clientThreadId: state.clientThreadId } : {}),
|
|
256
314
|
items: state.items,
|
|
315
|
+
...(state.providerOutputStart !== undefined ? { providerOutputStart: state.providerOutputStart } : {}),
|
|
257
316
|
...(state.providers ? { providers: state.providers } : {}),
|
|
258
317
|
};
|
|
259
318
|
const serialized = JSON.stringify(payload);
|
|
@@ -284,7 +343,7 @@ export function writeResponseSpillDurably(
|
|
|
284
343
|
try {
|
|
285
344
|
publishNoReplace(publishTempPath, destinationPath);
|
|
286
345
|
fsyncDirectoryBestEffort(dir);
|
|
287
|
-
|
|
346
|
+
unlinkEphemeral(publishTempPath);
|
|
288
347
|
tempPath = null;
|
|
289
348
|
return { version: 1, fileName, digest, payloadBytes: bytes.byteLength };
|
|
290
349
|
} catch (error) {
|
|
@@ -298,7 +357,7 @@ export function writeResponseSpillDurably(
|
|
|
298
357
|
try { closeSync(fd); } catch { /* best effort */ }
|
|
299
358
|
}
|
|
300
359
|
if (tempPath) {
|
|
301
|
-
try {
|
|
360
|
+
try { unlinkEphemeral(tempPath); } catch { /* best effort */ }
|
|
302
361
|
}
|
|
303
362
|
throw new Error("Response spill write failed");
|
|
304
363
|
}
|
|
@@ -306,6 +365,9 @@ export function writeResponseSpillDurably(
|
|
|
306
365
|
|
|
307
366
|
export function readResponseSpill(responseId: string, ref: ResponseSpillRef): ResponseSpillReadResult {
|
|
308
367
|
if (!validSpillRef(ref)) return { ok: false, reason: "corrupt" };
|
|
368
|
+
// Refuse before any read/parse: an oversized declared payload would otherwise
|
|
369
|
+
// materialize an unbounded transient (readFileSync + utf8 + JSON.parse).
|
|
370
|
+
if (ref.payloadBytes > responseSpillPayloadCap()) return { ok: false, reason: "too_large" };
|
|
309
371
|
const match = OWNED_SPILL_NAME.exec(ref.fileName);
|
|
310
372
|
if (!match
|
|
311
373
|
|| match[2] !== sha256(responseId).slice(0, 12)
|
|
@@ -376,7 +438,10 @@ export function recoverOrphanedResponseSpills(
|
|
|
376
438
|
try { stat = lstatSync(path); } catch { continue; }
|
|
377
439
|
if (!stat.isFile() || stat.isSymbolicLink() || Date.now() - stat.mtimeMs < graceMs) continue;
|
|
378
440
|
try {
|
|
379
|
-
|
|
441
|
+
// Orphaned publish temps get the full ephemeral release; stable
|
|
442
|
+
// orphaned spills keep destination-keyed timeout memos.
|
|
443
|
+
if (isOwnedTemp) unlinkEphemeral(path);
|
|
444
|
+
else unlink(path);
|
|
380
445
|
result.removed += 1;
|
|
381
446
|
result.bytesRemoved += stat.size;
|
|
382
447
|
} catch {
|