Remove the `AgentsPanel` (from the `agent_ui_v2` crate) and the utility
pane infrastructure from the codebase.
The Agents Panel was a separate panel gated behind the `agent-v2`
feature flag that was redundant with the existing Agent Panel. Utility
panes were a layout concept (secondary panes next to the editor,
separate from dock panels) whose only consumer was `AgentThreadPane` in
the Agents Panel.
### Changes
- Deleted the entire `agent_ui_v2` crate (`agents_panel.rs`,
`agent_thread_pane.rs`)
- Deleted `workspace/src/utility_pane.rs`
- Removed `UtilityPane`, `UtilityPaneHandle`, `UtilityPanePosition`,
`MinimizePane`, `ClosePane` from `workspace/src/dock.rs`
- Removed all utility pane fields, methods, and render blocks from
`workspace.rs`
- Removed all aside toggle code from `pane.rs` and `pane_group.rs`
- Removed `agents_panel_dock` setting from agent settings and
`default.json`
- Removed all `agent_ui_v2` references from `main.rs`, `zed.rs`, and
Cargo.toml files
- Cleaned up test code in `tool_permissions.rs` and `agent_ui.rs`
Closes AI-17
(No release notes because this was all feature-flagged.)
Release Notes:
- N/A
Move shell command parsing logic (`extract_commands` and supporting
code) from the agent crate into a new `shell_command_parser` crate so it
can be reused by `agent_servers` for ACP permission checking.
Release Notes:
- N/A
Builds on top of #48620 to add explicit test coverage for the exact
bypass scenarios reported by users:
- `rm -rf /etc/../` — path traversal via single parent dir that
normalizes to `/`
- `rm -rf --no-preserve-root /` — long flag without `=value` that could
bypass the old regex
- `rm --no-preserve-root -rf /` — long flag positioned before short
flags
- `rm / -rf --no-preserve-root` — trailing long flag without `=value`
after the path operand
- `sudo rm -rf /`, `sudo rm -rf /*`, `sudo rm -rf --no-preserve-root /`
— sudo-prefixed variants
All of these cases are already correctly blocked by the hardened regex
patterns and path normalization logic added in #48620. These tests
confirm that the reported bypasses are addressed and guard against
regressions.
Release Notes:
- N/A
This PR hardens the terminal tool's hardcoded security rules for
destructive commands like `rm -rf /`, and adds path normalization to
prevent traversal-based bypasses.
## Path normalization
Adds `normalize_path` which resolves `..`, `.`, and redundant path
separators, and `decide_permission_for_path` which checks permissions
against both raw and normalized paths (taking the most restrictive
result). This prevents attacks like `rm -rf /tmp/../../` which
previously bypassed the `rm -rf /` rule.
## rm command expansion
Adds `expand_rm_to_single_path_commands` which splits multi-argument rm
commands into individual single-path commands for checking. This catches
cases like `rm -rf /tmp /` where the dangerous path is the second
argument.
## Regex hardening
- **FLAGS**: Now accepts digits, underscores, and uppercase in long
flags (e.g. `--no-preserve-root`)
- **`--flag=value`**: Correctly matched as a single flag token
- **Trailing flags**: Handles GNU rm's acceptance of flags after path
operands (e.g. `rm / -rf`)
- **`--` marker**: Detects end-of-options bypass attempts (e.g. `rm -rf
-- /`)
- **Whitespace**: Handles tabs and other whitespace, not just spaces
## `$HOME`/`${HOME}` handling
Normalizes the suffix after `$HOME`/`${HOME}` variable references so
that traversal attacks like `rm -rf $HOME/./` or `rm -rf ${HOME}/foo/..`
are correctly detected.
Release Notes:
- Strengthened terminal security rules to detect path traversal attacks
in destructive commands like `rm -rf`.
This PR removes the `fn name()` default method from the `AgentTool`
trait (which just returned `Self::NAME`) and replaces all call sites
with direct use of the `NAME` associated constant. This lets us use it
as a single source of truth in macros that want to access it at compile
time.
This PR also replaces hardcoded tool name string literals throughout the
codebase with the corresponding `NAME` constants, so tool name
references stay in sync if a tool is renamed.
Intentionally not changed:
- **Display strings** like `"Thinking"` and `"Subagent"` (user-facing UI
labels)
- **Serde attributes** like `#[serde(rename = "thinking")]` (wire
format)
- **The test assertion** `assert_eq!(SubagentTool::NAME, "subagent")`
(replacing the literal would make it tautological)
- **MCP server names** like `"mcp:srv:terminal"` (not tool identifiers)
Release Notes:
- N/A
Expands the hardcoded security rules that block dangerous `rm` commands
in the agent terminal tool.
### New blocked patterns
- `rm -rf $HOME` / `rm -rf $HOME/` / `rm -rf ${HOME}` / `rm -rf
${HOME}/`
- `rm -rf .` / `rm -rf ./`
- `rm -rf ..` / `rm -rf ../`
- `rm -rf ~/` (previously only `rm -rf ~` was blocked)
### Flag handling improvements
- Simplified the flag character class from `[rRfF]` to `[rf]` since the
regex is already compiled with case-insensitive mode — less confusing,
same behavior.
- Added tests verifying that reversed flags (`-fr`), uppercase (`RM
-RF`), split flags (`-r -f`), and chained commands all get caught.
### Safe commands still allowed
Paths like `rm -rf ./build`, `rm -rf ~/Documents`, `rm -rf
$HOME/Documents`, `rm -rf ../some_dir`, and `rm -rf .hidden_dir` are
**not** blocked.
Release Notes:
- Auto-block a wider range of agent terminal commands, e.g. `rm -rf
$HOME` in addition to `rm -rf ~`
`brush-parser` handles `;` (sequential execution) and `|` (piping) which
all these shells use, so we can safely parse their commands for
`always_allow` pattern matching.
(No release notes because granular tool permissions haven't been
released yet.)
Release Notes:
- N/A
This change introduces hardcoded security rules for the terminal tool
that cannot be bypassed by any setting, including
`always_allow_tool_actions`.
## Currently Blocked Commands
- `rm -rf /` - Recursive deletion of root filesystem
- `rm -rf ~` - Recursive deletion of home directory
These rules are checked **before** the `always_allow_tool_actions`
global flag, ensuring they can never be bypassed. The rules also check
parsed sub-commands, so `ls && rm -rf /` is also blocked.
Release Notes:
- Certain known-bad tool uses are now automatically blocked, such as the
terminal tool attempting to run `rm -rf /` or `rm -rf ~`
## 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>
<img width="1110" height="280" alt="Screenshot 2026-01-28 at 3 35 52 PM"
src="https://github.com/user-attachments/assets/4d467e2c-2e7b-4ec7-bc87-6f0df8e667f0"
/>
<img width="1094" height="411" alt="Screenshot 2026-01-28 at 3 40 54 PM"
src="https://github.com/user-attachments/assets/f559df93-e72e-4457-ba1b-f7d6239f3285"
/>
Previously, if a user configured `^ls` as an always-allow pattern, an
attacker could craft a command like `ls && rm -rf /` which would be
auto-approved because the regex only matched the beginning of the
command string.
Now the command is parsed into individual sub-commands (`ls`, `rm -rf
/`) and EACH sub-command must match an allow pattern for auto-approval.
This prevents shell injection attacks using operators like:
- `&&` and `||` (boolean operators)
- `;` and `&` (sequential/background execution)
- `|` (pipes)
- Newlines
- Command substitution (`$()` and backticks)
- Process substitution (`<()` and `>()`)
## Matching Logic
- **always_deny**: if ANY sub-command matches, deny the entire command
- **always_confirm**: if ANY sub-command matches, require confirmation
(unless always_deny matched, in which case deny)
- **always_allow**: ALL sub-commands must match for auto-approval
(unless always_confirm or always_deny matched, in which case defer to
those)
- If parsing fails, or if the shell is unsupported, then always_allow is
disabled for this command
As usual, `always_allow_tool_actions` supercedes all of these. If it is
`true`, then we always allow all tool calls, no questions asked.
## Shell Compatibility
The shell parser only supports POSIX-like command chaining syntax (`&&`,
`||`, `;`, `|`).
**Supported shells:** Posix (sh, bash, dash, zsh), Fish 3.0+, PowerShell
7+/Pwsh, Cmd, Xonsh, Csh, Tcsh
**Unsupported shells:** Nushell (uses `and`/`or` keywords), Elvish (uses
`and`/`or` keywords), Rc (Plan 9 shell - no `&&`/`||` operators)
For unsupported shells:
- The "Always allow" UI options are hidden for the terminal tool
- If the user has `always_allow` patterns configured in settings, they
will see a `Deny` with an explanatory error message
(No release notes because granular tool permissions are behind a feature
flag.)
Release Notes:
- N/A
---------
Co-authored-by: Zed Zippy <234243425+zed-zippy[bot]@users.noreply.github.com>
Previously, `always_confirm` patterns would force confirmation even when
`always_allow_tool_actions` was set to true. This was counterintuitive
since the global setting should provide a way to skip all confirmations.
The new precedence order is:
1. **`always_deny`** - still blocks for security
2. **`always_allow_tool_actions`** - when true, allows all non-denied
actions
3. **`always_confirm`** - prompts if `always_allow_tool_actions` is
false
4. **`always_allow`** - allows without prompting
5. **`default_mode`** - fallback behavior
This means setting `always_allow_tool_actions=true` will now skip
confirmation prompts from `always_confirm` patterns and override
`default_mode: Deny` settings, while still respecting `always_deny`
patterns for security.
(No release notes because granular tool permissions are still
feature-flagged.)
Release Notes:
- N/A
This builds on https://github.com/zed-industries/zed/pull/46112 (which
should be merged first) and adds the permission evaluation logic and
integrates it with the terminal tool as part of the tool permissions
feature. This is a separate PR for [stacked
diffs](https://newsletter.pragmaticengineer.com/p/stacked-diffs) to make
review easier.
## Changes
- **Add `tool_permissions.rs`** with `decide_permission()` function that
implements:
- `deny > confirm > allow` precedence hierarchy (security-critical)
- Case-sensitive and case-insensitive regex matching
- Integration with existing `always_allow_tool_actions` setting
- Comprehensive unit tests (15 tests)
- **Integrate with terminal tool**:
- Commands matching deny rules are blocked immediately with an error
- Commands matching allow rules proceed without confirmation dialog
- Commands requiring confirmation show the dialog as before
- Added integration tests for deny and allow rule scenarios
Co-Authored-By: Claude Opus 4.5
Release Notes:
- N/A
---------
Co-authored-by: Amp <amp@ampcode.com>