# 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
Release Notes:
- Fixed heredoc commands failing with "syntax error: unexpected end of
file" in AI Agent shell execution
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Jakub Konka <kubkon@jakubkonka.com>
## Summary
This PR extends the `always_allow` tool permission patterns to work with
Nushell, Elvish, and Rc shells. Previously, these shells were
incorrectly excluded because they don't use `&&`/`||` operators for
command chaining. However, brush-parser can safely parse their command
syntax since they all use `;` for sequential execution.
## Changes
- Add `ShellKind::Nushell`, `ShellKind::Elvish`, and `ShellKind::Rc` to
`supports_posix_chaining()`
- Split `ShellKind::Unknown` into `ShellKind::UnknownWindows` and
`ShellKind::UnknownUnix` to preserve platform-specific fallback behavior
while still denying `always_allow` patterns for unrecognized shells
- Add comprehensive tests for the new shell support
- Clarify documentation about shell compatibility
## Shell Notes
- **Nushell**: Uses `;` for sequential execution. The `and`/`or`
keywords are boolean operators on values, not command chaining.
- **Elvish**: Uses `;` to separate pipelines. Does not have `&&` or `||`
operators. Its `and`/`or` are special commands operating on values.
- **Rc (Plan 9)**: Uses `;` for sequential execution and `|` for piping.
Does not have `&&`/`||` operators.
## Security
Unknown shells still return `false` from `supports_posix_chaining()`, so
`always_allow` patterns are denied for safety when we can't verify the
shell's syntax.
(No release notes because granular tool permissions are still
feature-flagged.)
Release Notes:
- N/A
---------
Co-authored-by: Zed Zippy <234243425+zed-zippy[bot]@users.noreply.github.com>
Closes#45211
This ensures that all sub-processes that were launched by the ACP server
are terminated. One scenario where this is easily reproducible:
- Start a new Claude Code ACP session
- Submit a prompt
- While Claude-code is still responding, start a new session
- The `claude-code` subprocess is leaked from the previous session (The
Claude-code SDK runs the Claude-code binary in a sub process)
This PR fixes this by using process groups on Unix.
It does not fix the process leaks on Windows yet (will follow up with
another PR)
Release Notes:
- Fixed an issue where subprocesses of ACP servers could be leaked after
starting a new session
Using `shlex` unconditionally is dangerous as it assumes the underlying
shell is POSIX which is not the case for PowerShell, CMD, or Nushell.
Therefore, whenever we want to quote the args we should utilise our
helper `util::shell::ShellKind::try_quote` which takes into account
which shell is being used to actually exec/spawn the invocation.
Release Notes:
- N/A
---------
Co-authored-by: Lukas Wirth <me@lukaswirth.dev>