Conversation
6d653cb to
e4932cd
Compare
|
This was generated by AI during triage. A defect in the change: The new Problems:
Suggested changes:
Checked against |
kshitijk4poor
left a comment
There was a problem hiding this comment.
Reviewed against current main (241605d), with the PR applied to a fresh worktree and exercised end-to-end (side-by-side against clean main). The heredoc-aware direction is genuinely good — heredoc bodies are data and the current regexes do false-positive on them — but as it stands this PR rejects or silently rewrites several valid commands that run fine on main today, so it can't merge as-is.
Confirmed regressions (each verified live, PR vs clean main):
- Arithmetic
&:x=$((5&3)); echo $x— runs on main, hard-rejected by the PR as "internal shell '&' backgrounding". Same for$((FLAGS & MASK)). - Bash
|&pipe-stderr:make |& tee build.log— runs on main, rejected. casefallthrough;&/;;&: rejected as internal backgrounding.- Parallel fan-out idiom with
background=true:job1 & job2 & waitran fine on main as a managed background call; the PR rejects it in both modes, killing single-call parallelism. - Backgrounded heredoc:
python3 - <<'PY' &… — positions are computed on the heredoc-stripped string but finality is checked on the raw command, so the body text after&makes it "internal" → rejected. (And if it were normalized,_normalize_final_shell_background_operatorreturns only the prefix, dropping the heredoc body entirely.)
Design concerns:
- Silent auto-flip changes deliberate behavior.
python3 -m http.server &(foreground) is blocked with guidance on main by design — the model must consciously re-send withbackground=true. The PR silently converts it to a managed background process, bypassingFOREGROUND_MAX_TIMEOUTand the long-lived-process guidance; a caller-suppliedtimeoutis quietly repurposed. AlsoA; B &backgrounds only B in a real shell, but the PR backgrounds the whole compound — a semantic change delivered as a warning. - Conflicts with live machinery. Main's
_rewrite_compound_background(the #68915 subshell-deadlock fix, called fromenvironments/base.pyandprocess_registry.py) becomes dead/contradictory on the terminal_tool path — the PR strips/rejects every&before the rewriter can see one, without removing or reconciling it. There's also open PR #68948 proposing to retire that rewriter, and #63788 which fixes the same heredoc false-positive class more narrowly. - No tests for ~286 lines of shell lexing, and the quote/comment scanning loop is written three times with already-divergent behavior (
_read_shell_tokenexists at terminal_tool.py:622 for this). - The lexer runs 2× (plus 3–4 heredoc-strip string rebuilds) on every terminal call — the hottest tool path in the agent.
Suggested path forward: the heredoc-masking insight is worth landing — but scoped to fixing the existing detection false-positives (what #63788 does), not adding a normalize/reject layer. If auto-normalization of a lone trailing & is wanted, it needs a design pass coordinated with #68948 (rewriter retirement), an &-classifier shared with _rewrite_compound_background instead of a parallel one, tests covering the shell grammar corners above, and an explicit decision on the auto-flip vs. block-with-guidance behavior.
Happy to review a narrowed v2. Thanks for the thorough writeup in the PR body — the related-PR analysis made this review much easier.
|
Thank you for the feedback @kshitijk4poor ! |
|
Good question — here's the plan on our side. The heredoc false-positive fix will land via #63788, since it's the earliest focused fix for that class — but not as-is: the review on that PR flagged bypass concerns (fake So please don't narrow this PR down to the heredoc fix — that would just duplicate #63788 and cost you both attribution. What would genuinely help:
I'd keep this PR open until the salvage lands (it's a useful reference), then close it with a pointer. |
|
Update: the salvage described above is now up as #84402 — #63788's fix cherry-picked onto current main (authorship preserved) plus a hardening commit that makes the heredoc masking conservative (quoted+terminated+single-command+non-shell-consumer only) and factors it into a shared @rroverin @elisam0 — edge-case review on #84402 is very welcome; you clearly explored the shell grammar corners here. The trailing- |
|
Closing in favor of #84402, which lands the heredoc false-positive fix this PR targeted (via a salvage of the earlier #63788, hardened into a shared conservative masking module). The verified regressions listed in the review above (arithmetic The trailing- |
|
Thank you @kshitijk4poor . We have done the edge case review discussed above in a new PR ready for review (small fix and some missing tests added): #89994 . |
What does this PR do?
Makes terminal background-command handling safe and predictable. Shell-level
&backgrounding can cause Hermes to track the shell rather than the actual child process, preventing reliable polling, waiting, and output streaming.The terminal tool now recognizes real, unquoted shell background operators while ignoring quoted text, comments, redirects, and heredoc bodies. A single trailing
&is normalized into Hermes-managedbackground=trueexecution with a warning. Internal backgrounding in compound commands, or an&used together withbackground=true, is rejected with instructions to split the work into managed terminal calls.Related Issue
Related PRs:
&inside heredoc bodies. This PR includes the same class of heredoc-aware detection, but also normalizes one final top-level&into Hermes-managedbackground=trueexecution and rejects unsafe internal or double backgrounding.&backgrounding and asks the model to split the work into separately managed terminal calls.Type of Change
Changes Made
tools/terminal_tool.pyfor heredoc declarations and top-level shell&operators.&&,&>, and heredoc bodies from backgrounding detection.&into managedbackground=trueexecution and returned a warning explaining the normalization.background=trueplus shell&, where Hermes cannot safely track the process.How to Test
terminal(command="sleep 1 &")and verify it is started as a managed background command with a normalization warning.terminal(command="sleep 1 & echo done")and verify it is rejected with guidance to split it into separate calls.&in quotes, comments, redirects, or heredoc content are not mistaken for backgrounding.Checklist
Code
fix(scope):,feat(scope):, etc.)pytest tests/ -qand all tests passDocumentation & Housekeeping
docs/, docstrings) — or N/Acli-config.yaml.exampleif I added/changed config keys — or N/ACONTRIBUTING.mdorAGENTS.mdif I changed architecture or workflows — or N/A