From 85177a29ecb665524d64a914e2d8d38a34236d57 Mon Sep 17 00:00:00 2001 From: metaphorics <152830360+metaphorics@users.noreply.github.com> Date: Wed, 5 Aug 2026 10:42:36 +0000 Subject: [PATCH] fix(coding-agent): fail loudly when no POSIX bash exists; add stdin-isolation regression test --- packages/coding-agent/src/eval/py/runner.py | 8 +++++++- .../test/eval/py/runner-shell-output.test.ts | 20 +++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/packages/coding-agent/src/eval/py/runner.py b/packages/coding-agent/src/eval/py/runner.py index c006dea1e..a87f6ab71 100644 --- a/packages/coding-agent/src/eval/py/runner.py +++ b/packages/coding-agent/src/eval/py/runner.py @@ -759,7 +759,13 @@ def _resolve_bash() -> str: found = shutil.which("bash") if found and "system32" not in found.lower(): return found - return "bash" + # WSL's System32 bash.exe runs in a separate Linux environment, so + # silently falling back to it would execute the cell somewhere the user + # did not intend; fail loudly instead. + raise RuntimeError( + "%%bash requires a POSIX bash, but none was found. " + "Install Git for Windows or add a non-WSL bash to PATH." + ) @cell_magic("bash") diff --git a/packages/coding-agent/test/eval/py/runner-shell-output.test.ts b/packages/coding-agent/test/eval/py/runner-shell-output.test.ts index 114725be2..44cfc7f9c 100644 --- a/packages/coding-agent/test/eval/py/runner-shell-output.test.ts +++ b/packages/coding-agent/test/eval/py/runner-shell-output.test.ts @@ -148,6 +148,26 @@ describe("Python runner shell output streaming", () => { expect(stdout).not.toContain("capturedChars=1048593"); }); + it("isolates !cmd children from the runner's stdin control channel", async () => { + // The runner's stdin carries the host's NDJSON frames. A child that + // inherits it can steal frames or block forever waiting for input; + // with stdin=DEVNULL a stdin-reading child sees immediate EOF instead. + const child = ["import sys", "data = sys.stdin.read()", "print('read=' + repr(data))"].join(";"); + const frames = await runCell( + [ + `result = !${pythonPath} -c ${shellQuote(child)}`, + "print('return=' + str(result.returncode) + ' lines=' + repr(list(result)))", + ].join("\n"), + ); + const stdout = frames + .filter(frame => frame.type === "stdout") + .map(frame => frame.data) + .join(""); + + expect(stdout).toContain("read=''"); + expect(stdout).toContain("return=0"); + }); + it("streams newline-free %%bash output without waiting for EOF", async () => { const child = [ "import sys,time",