From 8e18ab0cd7cd8471a84d8d337bedeaefadd79a1e Mon Sep 17 00:00:00 2001 From: procr1337 <193802945+procr1337@users.noreply.github.com> Date: Wed, 5 Aug 2026 10:16:06 +0200 Subject: [PATCH] util: Fix stdin redirect for ShellBuilder (#62078) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Objective This is a follow-up to my own PR #59270 which fixed the hang it targeted (#55042) only partially and broke `ShellBuilder::build` for fish. **The hang is only half fixed.** The agent's terminal tool wraps commands as `sh -i -c 'exec '`. The redirect sits on the same line as the command, but POSIX shells parse an entire line before executing any of it. So when `` has a syntax error, the whole line is discarded — including the `exec` that was supposed to close stdin. Interactive `dash` then recovers from the error, finds stdin still attached to the PTY, and sits at a `$` prompt until the user presses Ctrl+D. That means #59270 only helped when the redirect had already run, i.e. multi-line commands whose error is on a later line. A single-line command still hangs, including `cat <(echo hi)`, the example from that PR's own description. Probably prompts or tool definitions changed since that PR landed which now make it more likely again to run into this issue, at least with Anthropic models. **fish runs no command at all.** #59270 also applied the `exec ' ``` That makes it a separate parse-and-execute unit, so it runs before the command line is parsed. Any syntax error is then reported by a shell that already has `/dev/null` on stdin, and it exits instead of prompting. For fish, go back to `begin; ; end { - combined_command.insert_str(0, "exec { + // Perform the STDIN redirection prior to the actual + // command on a separate line, so that it is already + // active if the command contains a syntax error. + // Otherwise, with -i, dash will fall back to an + // interactive shell in this case. + combined_command.insert_str(0, "exec { + combined_command.insert_str(0, "begin; "); + combined_command.push_str("; end { - combined_command.insert_str(0, "exec { + combined_command.insert_str(0, "exec { + combined_command.insert_str(0, "begin; "); + combined_command.push_str("; end