fix(agent-core): bound harness exec stdout/stderr at 16 MiB#95195
fix(agent-core): bound harness exec stdout/stderr at 16 MiB#95195Pick-cat wants to merge 7 commits into
Conversation
|
Codex review: needs maintainer review before merge. Reviewed July 10, 2026, 5:39 AM ET / 09:39 UTC. Summary PR surface: Source +43, Tests +49. Total +92 across 4 files. Reproducibility: yes. Current main directly accumulates every child-output chunk without a limit, and the PR supplies a real-process negative control demonstrating that behavior. Review metrics: 1 noteworthy metric.
Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Risk before merge
Maintainer options:
Next step before merge
Maintainer decision needed
Security Review detailsBest possible solution: Keep capture bounding in the Node execution environment with independent stream limits and uninterrupted callbacks, then land it after the feature owner explicitly accepts and documents the large-output default change. Do we have a high-confidence way to reproduce the issue? Yes. Current main directly accumulates every child-output chunk without a limit, and the PR supplies a real-process negative control demonstrating that behavior. Is this the best way to solve the issue? Yes technically. The execution environment is the narrowest owner that can bound retained output while preserving complete streaming callbacks, but maintainers must accept the deliberate default compatibility change. AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against abbd5ae3ea73. Label changesLabel justifications:
Evidence reviewedPR surface: Source +43, Tests +49. Total +92 across 4 files. View PR surface stats
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
Review history (6 earlier review cycles)
|
|
Pushed
On proof: I don't have a live macOS 26 (Tahoe) host. The off-Darwin path is verified on a real host, the @clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. |
101fb73 to
ce0d66c
Compare
|
@clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. |
9399fec to
af08225
Compare
|
@clawsweeper re-review Branch rebased to latest main. Proof and patch improvements since last review:
CI: |
|
🦞🧹 I asked ClawSweeper to review this item again. |
|
@clawsweeper re-review |
|
@clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. |
Replace unbounded stdout/stderr string accumulation with a byte-capped collector that truncates and appends "[output truncated]" when output exceeds maxOutputBytes (default 16 MiB). Prevents OOM from runaway child process output in the agent harness exec path. Co-Authored-By: Claude Opus 4.7 <[email protected]>
Co-Authored-By: Claude Opus 4.7 <[email protected]>
Stop accumulating output when cap is exceeded but let the process continue to natural exit or timeout. Fixes the merge-risk: availability concern — truncated output no longer takes down the child process. Co-Authored-By: Claude Opus 4.7 <[email protected]>
…t type - Separate stdoutTruncated/stderrTruncated so stderr is not dropped after stdout crosses the cap - Use truncateToBytes (UTF-8 character iterator) instead of chunk.slice(0, N) which slices by UTF-16 code units - Expose maxOutputBytes on ExecutionEnvExecOptions so all Shell implementations share the contract Co-Authored-By: Claude Opus 4.7 <[email protected]>
…process file
The output bounding tests use real child process spawn calls, but
the vi.mock("node:child_process") in nodejs.test.ts intercepted all
spawn calls and returned undefined, causing "Cannot read properties
of undefined (reading 'stdout')" errors.
Move them to a separate file (nodejs.exec-bounding.test.ts) that does
not mock child_process, keeping the mocked stream error tests and the
real-process output bounding tests isolated.
What Problem This Solves
Fixes unbounded stdout/stderr string accumulation in
NodeExecutionEnv.exec(). Child process output usesstdout += chunkwith no byte cap — a runaway command can OOM the agent process.Why This Change Was Made
Add a 16 MiB per-stream cap with a
maxOutputBytesoverride on the sharedExecutionEnvExecOptionstype. Design:truncateToBytes()stops at exact UTF-8 byte boundary, not UTF-16 code unitsonStdout/onStderrcontinue receiving chunks even after capture is capped — streaming parsers are not interruptedmaxOutputBytesonExecutionEnvExecOptionsso allShellimplementations share the optionappendBoundedChunk()removes the duplicated stdout/stderr bounding logicUser Impact
Runaway commands can no longer OOM the agent process. Normal commands are unaffected. Streaming callbacks work end-to-end regardless of capture size.
Real behavior proof
Environment: Linux, Node 24, local
.mtsproof script driving the realNodeExecutionEnv.exec()against real child processes. The proof script is run locally and is not committed to the repo.Fix (19 passed, 0 failed):
Negative control (origin/main, same script):
Evidence
Vitest (real child process spawn)
Review fixes applied
CapturedStreamstatetruncateToBytes()char-levelBuffer.byteLengthiteratormaxOutputBytesnot on shared typeExecutionEnvExecOptionsonStdout/onStderrfire for every chunk regardless of capappendBoundedChunk()helper