agent: Fix shell hang on shell syntax errors with terminal tool usage (#59270)
# Objective When the agent's terminal tool runs a command with an unsupported shell syntax (e.g. process substitution `<()` in dash), the shell prints an error and then drops into interactive mode. It shows a `$` prompt and hangs waiting for input, requiring the user to press Ctrl+D to continue. This happens because the stdin redirect wraps the command in a subshell `(...) </dev/null`, which only closes stdin for the inner command. The outer shell still has its stdin connected to the PTY, so after a syntax error it reads from the PTY and waits for more input. ## Solution Replace the subshell wrapping with an `exec`-level redirect. Instead of: ```sh sh -i -c '(command\n) </dev/null' ``` We now produce: ```sh sh -i -c 'exec </dev/null; command' ``` `exec </dev/null;` closes stdin for the **entire shell process**. If the command fails with a syntax error, the shell immediately gets EOF from stdin and exits cleanly instead of prompting for more input. The `-i` flag is preserved so that shells which support it still source their interactive init files and enable job control. This change applies to `ShellKind::Posix` (sh/bash/dash/zsh) and `ShellKind::Fish` in both `ShellBuilder::build()` and `ShellBuilder::build_no_quote()`. ## Testing - Manually verified that `cat <(echo hi)` (process substitution in dash) no longer hangs: the shell exits immediately with the syntax error ## Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable --- Release Notes: - Fixed agent terminal tool hanging with a `$` prompt when commands encounter syntax errors or unsupported shell features like process substitution
This commit is contained in:
1 parent
ae84331f5a
commit
b600f44d69
1 file changed
+8
-12
@@ -99,12 +99,10 @@ impl ShellBuilder {
|
||||
});
|
||||
if self.redirect_stdin {
|
||||
match self.kind {
|
||||
ShellKind::Fish => {
|
||||
combined_command.insert_str(0, "begin; ");
|
||||
combined_command.push_str("; end </dev/null");
|
||||
ShellKind::Fish | ShellKind::Posix => {
|
||||
combined_command.insert_str(0, "exec </dev/null; ");
|
||||
}
|
||||
ShellKind::Posix
|
||||
| ShellKind::Nushell
|
||||
ShellKind::Nushell
|
||||
| ShellKind::Csh
|
||||
| ShellKind::Tcsh
|
||||
| ShellKind::Rc
|
||||
@@ -145,12 +143,10 @@ impl ShellBuilder {
|
||||
});
|
||||
if self.redirect_stdin {
|
||||
match self.kind {
|
||||
ShellKind::Fish => {
|
||||
combined_command.insert_str(0, "begin; ");
|
||||
combined_command.push_str("; end </dev/null");
|
||||
ShellKind::Fish | ShellKind::Posix => {
|
||||
combined_command.insert_str(0, "exec </dev/null; ");
|
||||
}
|
||||
ShellKind::Posix
|
||||
| ShellKind::Nushell
|
||||
ShellKind::Nushell
|
||||
| ShellKind::Csh
|
||||
| ShellKind::Tcsh
|
||||
| ShellKind::Rc
|
||||
@@ -286,7 +282,7 @@ mod test {
|
||||
.build(Some("echo".into()), &["test".to_string()]);
|
||||
|
||||
assert_eq!(program, "fish");
|
||||
assert_eq!(args, vec!["-i", "-c", "begin; echo test; end </dev/null"]);
|
||||
assert_eq!(args, vec!["-i", "-c", "exec </dev/null; echo test"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -302,7 +298,7 @@ mod test {
|
||||
assert_eq!(program, "sh");
|
||||
assert_eq!(
|
||||
args,
|
||||
vec!["-i", "-c", "(cat <<EOF\nhello\nEOF\n) </dev/null"]
|
||||
vec!["-i", "-c", "exec </dev/null; cat <<EOF\nhello\nEOF"]
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user