fix(file-transfer): keep bounded stderr tail UTF-16 safe#104151
Conversation
|
Codex review: needs maintainer review before merge. Reviewed July 11, 2026, 11:14 AM ET / 15:14 UTC. Summary PR surface: Source +20, Tests +91. Total +111 across 6 files. Reproducibility: yes. Current main's raw bounded slices deterministically leave a lone surrogate at the stated code-unit boundaries, and the affected production tar stderr paths are directly traceable. Review metrics: none identified. 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: Merge the focused exact-head fix after routine maintainer review; it preserves existing caps and error semantics while making every affected file-transfer truncation boundary surrogate-safe. Do we have a high-confidence way to reproduce the issue? Yes. Current main's raw bounded slices deterministically leave a lone surrogate at the stated code-unit boundaries, and the affected production tar stderr paths are directly traceable. Is this the best way to solve the issue? Yes. The PR applies the repository's existing exported safe-slice contract at both streaming and final projection boundaries, covers every matching file-transfer path, and does not alter limits or unrelated behavior. AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against f5060701881b. Label changesLabel justifications:
Evidence reviewedPR surface: Source +20, Tests +91. Total +111 across 6 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)
|
1e2d2f0 to
1f6ff7d
Compare
|
@clawsweeper re-review Branch cleanup for this round:
PR body updated with caller-path evidence. |
|
🦞🧹 I asked ClawSweeper to review this item again. Re-review progress:
|
1f6ff7d to
5d3801b
Compare
|
@clawsweeper re-review — added real /usr/bin/tar subprocess proof (Cases A–C) to Real behavior proof section. |
|
🦞🧹 I asked ClawSweeper to review this item again. Re-review progress:
|
5d3801b to
7b3d27f
Compare
|
@clawsweeper re-review Boundary-specific real behavior proof added on head
Caller-path fixtures updated so the 200-char projection actually bisects |
|
🦞🧹 I asked ClawSweeper to review this item again. Re-review progress:
|
|
Merge-ready at Clean review with no actionable findings. No compatibility, configuration, protocol, persisted-state, or public API changes identified. This is the best narrow fix: it repairs both file-transfer stderr accumulation and final error projection at the plugin owner boundary. Exact-head CI and real-behavior proof are green. This complete fix supersedes #104098, which addresses accumulation but not the final projection boundaries. Thanks @hugenshen! |
Additional instructions
MUST: Keep Allow edits from maintainers enabled for this PR so maintainers
can help update the branch when needed.
What Problem This Solves
Fixes an issue where file-transfer tar/dir-fetch stderr tail truncation can produce invalid Unicode when paths or archive listings contain emoji or other supplementary-plane characters.
appendBoundedTextTail()capped rawchild.stderrchunks withnext.slice(-maxChars)and failure reasons still used rawstderr.slice(-200/-300)at projection time. Tar stderr is unfiltered, so filenames like📊report.csvcan land on a UTF-16 surrogate boundary and leave a lone high/low surrogate in error text surfaced to agents/operators.Concrete example: with production ring cap
4096and input"x"×4095 + "🤖" + "y"×4095, raw.slice(-4096)keepsU+DD16(lone low surrogate) without its high half; with reason cap200and input"🤖" + "f"×199, raw.slice(-200)has the same failure.Why This Change Was Made
Extract shared helpers in
extensions/file-transfer/src/shared/append-bounded-text-tail.ts:appendBoundedTextTail— UTF-16-safe ring buffer for streaming stderrprojectBoundedTextTail— UTF-16-safe final projection for error reasonsBoth use
sliceUtf16Safefromopenclaw/plugin-sdk/text-utility-runtime, matching the SCP stderr fix (#103757) pattern. Ring-buffer sizes and tar/dir-fetch behavior are unchanged; only truncation boundaries are surrogate-safe end-to-end.User Impact
Before:
dir_fetch/ archive-list failures can surface replacement glyphs or invalid Unicode in agent/operator error text when stderr mentions emoji paths near the 4,096-char ring-buffer cap or 200/300-char error projection.After: File-transfer stderr tails and surfaced failure reasons remain well-formed Unicode; boundary emoji are dropped whole instead of split.
Evidence
extensions/file-transfer/src/shared/append-bounded-text-tail.ts— shared UTF-16-safe ring buffer + projection helpersextensions/file-transfer/src/tools/dir-fetch-tool.ts— tar list/budget/unpack stderr paths use both helpersextensions/file-transfer/src/shared/node-invoke-policy.ts— archive list stderr path uses both helpersappend-bounded-text-tail.test.ts, caller-path tests indir-fetch-tool.test.tsandnode-invoke-policy.stream-errors.test.ts(fixtures place🤖so rawslice(-200)starts on the low surrogate)Real behavior proof
dir_fetchtoolpreValidateTarball/ nodelistDirFetchArchiveEntries→spawn(tar, …)→child.stderrchunks →appendBoundedTextTailring buffer →projectBoundedTextTailin failure reason returned to agent/operator.consumeChildOutput(production4096/200caps engineered to bisect🤖), plus real/usr/bin/tarwith an emoji missing path.sliceUtf16Safefromopenclaw/plugin-sdk/text-utility-runtime; provider/channel constraints N/A because this is plugin-local tar stderr handling.fix/file-transfer-bounded-tail-utf16,/usr/bin/tar.Observed result after fix: Case A shows raw production
4096ring starts on lone low surrogateU+DD16while patchedappendBoundedTextTaildrops that half and retains a well-formedy×4095 tail. Case B shows rawslice(-200)reason projection unpaired whileprojectBoundedTextTaildrops🤖and keepsf×199. Case C runs real/usr/bin/tarwith📊missing.csvthrough the same helpers at the 4,096 ring cap withunpaired_surrogates=false.What was not tested: Live
dir_fetch/ node archive-list on a remote host; Linux GNU tar stderr encoding differences.Fix classification: Root cause fix.
AI-assisted (Cursor)
I understand what the code does
Change is focused and does not mix unrelated concerns
Single commit on
upstream/main; no bundledscripts/or release-script changes