fix(agents): preserve ANSI sanitizer state across bash chunks#103706
Conversation
42decc9 to
4aab25d
Compare
|
Codex review: needs maintainer review before merge. Reviewed July 19, 2026, 2:29 AM ET / 06:29 UTC. Summary PR surface: Source +237, Tests +149. Total +386 across 7 files. Reproducibility: yes. The linked report gives a deterministic current-main callback-split probe, and the PR discussion includes an A/B result where the regression test fails against the base implementation and passes with this change. Review metrics: none identified. Stored data model Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Next step before merge
Security Review detailsBest possible solution: Land the bounded terminal-core sanitizer after the current-head required checks and final owner review confirm the merge result remains clean; keep the existing one-shot sanitizer behavior unchanged. Do we have a high-confidence way to reproduce the issue? Yes. The linked report gives a deterministic current-main callback-split probe, and the PR discussion includes an A/B result where the regression test fails against the base implementation and passes with this change. Is this the best way to solve the issue? Yes. Keeping bounded parser state in terminal-core and instantiating it once per bash command is the narrowest maintainable fix because it preserves streaming, existing output bounds, and the one-shot sanitizer contract. AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against 7055ed578d08. Label changesLabel changes:
Label justifications:
Evidence reviewedPR surface: Source +237, Tests +149. Total +386 across 7 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 (23 earlier review cycles; latest 8 shown)
|
|
Requesting changes at head Thanks for tackling the chunk-boundary ANSI leak. The stateful transform hook is the right boundary, but this implementation is not safe to merge yet because two P1 issues remain:
Please replace raw pending payload accumulation with bounded parser state, cover the full existing |
4aab25d to
00ee805
Compare
|
Updated this PR after the maintainer/ClawSweeper feedback. What changed:
Local validation on head
Note: @clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. |
00ee805 to
cb4b7dd
Compare
|
Added real after-fix process proof on the rebased head. Current head: Real behavior proof, outside the Vitest harness: What this proves:
Validation on this rebased head:
@clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. Re-review progress:
|
|
Correction for the previous proof comment: the shell interpreted Markdown backticks while posting. This is the clean proof text for the exact pushed head. Current head: cb4b7dd on top of upstream main 2f9cb92. Real behavior proof, outside the Vitest harness: PROOF_OPENCLAW_BASH_STREAM_ANSI=PASS What this proves:
Validation on this rebased head:
@clawsweeper re-review |
cb4b7dd to
0b41953
Compare
|
Updated the PR to address the latest ClawSweeper P1 findings. Current head: 0b41953 on top of upstream main b5251c4. Repairs:
Real child-process proof on this head: PROOF_OPENCLAW_BASH_STREAM_ANSI_REPAIR=PASS Validation on this exact head:
@clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. Re-review progress:
|
0b41953 to
ad441fe
Compare
|
Rebased onto current upstream main 04f945f and force-pushed the branch. Current head: ad441fe. Exact-head focused validation after this rebase:
The previous repair proof remains the same on this rebased head: finite parser state for incomplete compatibility controls, corrected OSC ESC+BEL/C1-ST handling, and real child_process.spawn proof for split output callbacks. @clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. Re-review progress:
|
b2eda7b to
665289d
Compare
|
@clawsweeper re-review Rebased onto current upstream/main. Exact head: Evidence:
|
|
🦞🧹 I asked ClawSweeper to review this item again. Re-review progress:
|
665289d to
96c338c
Compare
|
@clawsweeper re-review Rebased onto current upstream/main. Exact head: Evidence:
|
|
🦞🧹 I asked ClawSweeper to review this item again. Re-review progress:
|
96c338c to
4e8dd26
Compare
|
@clawsweeper re-review Rebased onto current upstream/main to pick up the upstream correction for the unrelated package-acceptance workflow assertion that failed compact shard 7. Exact head: Evidence:
|
|
🦞🧹 I asked ClawSweeper to review this item again. Re-review progress:
|
4e8dd26 to
fd973ed
Compare
|
@clawsweeper re-review Rebased onto current upstream/main. Exact head: |
|
🦞🧹 I asked ClawSweeper to review this item again. Re-review progress:
|
Keep incremental parser state in the canonical terminal owner, avoid a second sanitizer pass, and leave OutputAccumulator and public terminal APIs unchanged.\n\nCo-authored-by: Jicheng Xu <[email protected]>
fd973ed to
ab6a0de
Compare
|
Merged via squash.
|
…1364) The local exec runtime sanitized each stdout/stderr chunk with the stateless `sanitizeBinaryOutput`, so an escape sequence straddling a read boundary was escaped into visible text instead of being consumed. The remote bash path already uses a per-stream parser via `createStreamingBinaryOutputSanitizer` (#103706); this applies the same treatment to the local path, with separate parsers for stdout and stderr so neither stream can consume the other's pending sequence.
…aw#103706) * fix(agents): preserve ANSI sanitizer state across bash chunks * fix(agents): harden streaming ANSI sanitization Keep incremental parser state in the canonical terminal owner, avoid a second sanitizer pass, and leave OutputAccumulator and public terminal APIs unchanged.\n\nCo-authored-by: Jicheng Xu <[email protected]> --------- Co-authored-by: Peter Steinberger <[email protected]>
…nclaw#111364) The local exec runtime sanitized each stdout/stderr chunk with the stateless `sanitizeBinaryOutput`, so an escape sequence straddling a read boundary was escaped into visible text instead of being consumed. The remote bash path already uses a per-stream parser via `createStreamingBinaryOutputSanitizer` (openclaw#103706); this applies the same treatment to the local path, with separate parsers for stdout and stderr so neither stream can consume the other's pending sequence.
Closes #103668
What Problem This Solves
When bash output splits ANSI or OSC controls across stdout callbacks, stateless per-chunk stripping can leak control fragments into streamed output, final results, and spill files.
The maintained patch keeps a bounded incremental parser in terminal-core and preserves one instance for each bash command. One-shot stripping remains unchanged, the existing residual-control policy runs after the incremental parser without a second ANSI pass, and no new public or OutputAccumulator API is introduced.
Evidence
Exact rebased head:
ab6a0de3649ce6fa4cafc26fa9462afd282fc2denode scripts/run-vitest.mjs packages/terminal-core/src/ansi.test.ts src/agents/shell-utils.test.ts src/agents/sessions/bash-executor.test.ts: 82 passed, 1 skipped across two shards.