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",