From f803f66339aac2ec48ccf230facef856bef003c8 Mon Sep 17 00:00:00 2001 From: Teknium <127238744+teknium1@users.noreply.github.com> Date: Sat, 28 Mar 2026 14:43:41 -0700 Subject: [PATCH] fix(terminal): avoid merging heredoc EOF with fence wrapper (#3598) One-shot local execution built `printf FENCE; ; __hermes_rc=...`, so a command ending in a heredoc produced a closing line like `EOF; __hermes_rc=...`, which is not a valid delimiter. Bash then treated the rest of the wrapper as heredoc body, leaking it into tool output (e.g. gh issue/PR flows). Use newline-separated wrapper lines so the delimiter stays alone and the trailer runs after the heredoc completes. Co-authored-by: kshitijk4poor <82637225+kshitijk4poor@users.noreply.github.com> --- tests/tools/test_local_persistent.py | 12 ++++++++++++ tools/environments/local.py | 15 ++++++++++----- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/tests/tools/test_local_persistent.py b/tests/tools/test_local_persistent.py index b20cca5be..5b9ce2e23 100644 --- a/tests/tools/test_local_persistent.py +++ b/tests/tools/test_local_persistent.py @@ -63,6 +63,18 @@ class TestLocalOneShotRegression: assert r["output"].strip() == "" env.cleanup() + def test_oneshot_heredoc_does_not_leak_fence_wrapper(self): + """Heredoc closing line must not be merged with the fence wrapper tail.""" + env = LocalEnvironment(persistent=False) + cmd = "cat <<'H_EOF'\nheredoc body line\nH_EOF" + r = env.execute(cmd) + env.cleanup() + assert r["returncode"] == 0 + assert "heredoc body line" in r["output"] + assert "__hermes_rc" not in r["output"] + assert "printf '" not in r["output"] + assert "exit $" not in r["output"] + class TestLocalPersistent: @pytest.fixture diff --git a/tools/environments/local.py b/tools/environments/local.py index 8ee794e3b..8cd416efa 100644 --- a/tools/environments/local.py +++ b/tools/environments/local.py @@ -391,12 +391,17 @@ class LocalEnvironment(PersistentShellMixin, BaseEnvironment): effective_stdin = stdin_data user_shell = _find_bash() + # Newline-separated wrapper (not `cmd; __hermes_rc=...` on one line). + # A trailing `; __hermes_rc` glued to `<