---
url: /wave-agent/specs/core/tool-permission-system.md
description: 权限规则通配符匹配、复合命令结构感知判定、安全区域/附加目录、MCP 工具权限与 `acceptEdits`/`dontAsk` 等模式
---

# 功能规格说明：工具权限系统

## 概述

工具权限系统为 Wave agent 提供了强大的安全层，确保潜在的破坏性或敏感操作经过用户授权。它支持多种权限模式、带通配符的细粒度规则匹配、Shell 管道的安全分解、对特定工具或路径的显式拒绝、跨用户和项目作用域的持久化配置、交互式信任机制，以及文件操作的"安全区域"。

## 用户场景与测试

### 用户故事：带确认的默认安全模式（优先级：P1）

用户运行 Wave CLI 不带任何特殊标志，系统在执行任何潜在的破坏性操作（如文件编辑或 bash 命令）之前提示确认。

**验收场景**：

1. **假设**用户运行 Wave CLI 不带标志，**当** Wave 尝试编辑文件时，**则**出现确认提示，询问"是否继续？"并提供允许或修改请求的选项。
2. **假设**显示了确认提示，**当**用户选择"是"时，**则**操作正常执行。
3. **假设**显示了确认提示，**当**用户输入替代指令时，**则** Wave 通过工具结果字段接收新指令，而不是执行原始操作。
4. **假设** AI 返回多个受限工具调用，**当**每个工具调用被执行时，**则**每个调用都会出现单独的顺序确认提示。

### 用户故事：高级用户的绕过模式（优先级：P2）

高级用户使用 `--dangerously-skip-permissions` 运行 Wave CLI 以绕过所有权限检查，实现不间断操作。

**验收场景**：

1. **假设**绕过模式已启用，**当** Wave 尝试任何受限操作时，**则**不出现确认提示，操作立即执行。

### 用户故事：命令的通配符匹配（优先级：P1）

作为用户，我希望通过使用通配符指定通用模式来允许一组相关命令，这样我就不必在权限中列出每个命令的每个变体。

**验收场景**：

1. **假设** `permissions.allow` 包含 `Bash(git commit *)`，**当** agent 尝试运行 `Bash(git commit -m "initial commit")` 时，**则**操作被允许。
2. **假设** `permissions.allow` 包含 `Bash(git * main)`，**当** agent 尝试运行 `Bash(git push origin main)` 时，**则**操作被允许。

### 用户故事：智能通配符启发式（优先级：P1）

作为用户，我希望信任 `npm install lodash` 这样的命令，这样当我运行 `npm install express` 时不会再被提示。

**验收场景**：

1. **假设**系统提示 `npm install lodash`，**当**用户选择"是，不再询问"时，**则**系统应该建议一个智能通配符模式（如 `npm install *`）并保存。
2. **假设** `npm install *` 已被信任，**当**用户运行 `npm install express` 时，**则**它立即执行而不提示。

### 用户故事：分解和验证链式命令（优先级：P1）

作为用户，我希望当且仅当链中的每个单独命令都已被允许时，系统自动允许复杂命令（使用 `&&`、`|` 等）。

**验收场景**：

1. **假设** `permissions.allow` 包含 `cd /tmp/*` 和 `ls`，**当**用户执行 `cd /tmp/test && ls` 时，**则**系统应该自动允许该命令。
2. **假设** `permissions.allow` 包含 `cd /tmp/*` 但不包含 `rm *`，**当**用户执行 `cd /tmp/test && rm -rf /` 时，**则**系统不得自动允许，应该提示权限确认。
3. **假设** `permissions.allow` 包含 `Bash(node scripts*)` 但不含任何 git 规则，且内置默认允许规则包含 `Bash(git diff*)`，**当**用户执行 `node scripts/version.js && git diff --stat` 时，**则**系统应该自动允许该命令——链中各段可分别由不同规则来源（用户规则、内置默认规则等）覆盖，不要求单一规则来源覆盖所有段。

### 用户故事：拒绝规则与优先级（优先级：P1）

作为注重安全的用户，我希望明确禁止 agent 使用某些工具或访问特定路径，即使它们在其他情况下会被允许。

**验收场景**：

1. **假设** `permissions.deny` 包含 `["Bash"]`，**当** agent 尝试运行任何 bash 命令时，**则**系统必须阻止执行。
2. **假设** `permissions.allow` 包含 `["*"]` 且 `permissions.deny` 包含 `["Bash"]`，**当** agent 尝试运行 bash 命令时，**则**系统必须拒绝请求，因为拒绝规则优先。

### 用户故事：基于路径的权限（优先级：P1）

作为用户，我希望通过为操作文件路径的工具定义拒绝规则来防止 agent 访问特定文件（如 `.env` 文件）。

**验收场景**：

1. **假设** `permissions.deny` 包含 `["Read(**/.env)"]`，**当** agent 尝试读取任何目录中名为 `.env` 的文件时，**则**系统必须拒绝请求。

### 用户故事：内置安全命令与只读命令自动放行（优先级：P2）

作为用户，我希望常见的只读命令（如 `cat`、`sed`、`grep`、`awk`、`jq`、`find`）默认自动允许执行而无需逐次确认，以便代理在探查代码库时不被频繁打断；但能改写文件系统或执行任意命令的变体（如 `sed -i`、`find -delete`、写重定向、进程替换）仍须提示确认。只读命令（含 `cd`、`ls`、`cat`、`grep` 等）访问安全区域（工作目录 + 附加目录）之外的路径时须提示确认（对齐 Claude Code 的路径范围检查）；路径在安全区域内、或命令不访问任何文件系统路径时自动允许。命令替换（`$( )`/反引号）与 shell 控制结构（`for`/`if`/`while`/`case` 等）包裹的命令，其嵌套命令按「复合命令与命令替换的结构感知判定」用户故事独立判定，不因外层语法而整体禁止。

**验收场景**：

1. **假设** CWD 是 `/home/user/project`，**当**用户执行 `cd src` 时，**则**系统应该自动允许。
2. **假设** CWD 是 `/home/user/project`，**当**用户执行 `cd /etc` 时，**则**系统不得自动允许。
3. **假设**任何目录，**当**用户执行 `find . -name "*.ts"` 时，**则**系统应该自动允许。
4. **假设**任何目录，**当**用户执行 `find . -delete` 时，**则**系统不得自动允许，必须提示权限确认。
5. **假设**任何文件，**当**用户执行 `sed -n '19631,19900p' file.d.ts` 时，**则**系统应该自动允许（输出到 stdout，不修改文件）。
6. **假设**任何文件，**当**用户执行 `sed -i 's/old/new/' file.txt` 时，**则**系统不得自动允许（原地编辑即写操作），必须提示权限确认。
7. **假设**任何文件，**当**用户执行 `awk '{print $1}' file` 或 `jq '.x' file.json` 时，**则**系统应该自动允许。
8. **假设**任何命令，**当**用户执行 `cat $(rm important)` 或 `grep x \`whoami\``时，**则**系统不得自动允许（命令替换内嵌套`rm`/`whoami`非只读命令，须独立判定后提示确认；进程替换`<(...)`/`>(...)\` 一律不得自动允许），必须提示权限确认。
9. **假设**链式命令 `sed -n '1,10p' a.txt | grep foo`，**当** 执行时，**则** 系统应该自动允许（每段均为只读）。
10. **假设**任何 git 仓库，**当**用户执行 `git -C /path/to/repo status`、`git -C /path/to/repo diff --stat` 或 `git -C /path/to/repo log --oneline` 时，**则** 系统应该自动允许（`git` 的全局作用域选项如 `-C <path>`、`-c <key>=<value>`、`--git-dir <path>` 只改变目标仓库或配置，不改变子命令的只读属性；匹配 `Bash(git status*)` 等规则时忽略这些前缀）。
11. **假设**任何目录，**当**用户执行 `git -C /path/to/repo commit -m "msg"`、`git -C /path/to/repo push` 或 `git -C /path/to/repo branch -D feature` 时，**则** 系统不得自动允许，必须提示权限确认。
12. **假设** CWD 是 `/home/user/project`，**当**用户执行 `ls src` 时，**则**系统应该自动允许（相对路径解析后在工作目录内）。
13. **假设** CWD 是 `/home/user/project`，**当**用户执行 `ls /etc` 时，**则**系统不得自动允许，必须提示权限确认（只读命令访问工作目录外路径）。
14. **假设** CWD 是 `/home/user/project`，**当**用户执行 `cat /etc/passwd` 或 `grep root /etc/passwd` 时，**则**系统不得自动允许，必须提示权限确认（只读命令访问工作目录外路径，与 `ls` 一致）。
15. **假设** CWD 是 `/home/user/project`，**当**用户执行 `cat ../secret.txt` 时，**则**系统不得自动允许（`..` 解析到真实路径后在工作目录外）。
16. **假设** `permissions.additionalDirectories` 包含 `/data/exports`，**当**用户执行 `ls /data/exports` 时，**则**系统应该自动允许（路径在附加目录内，属于安全区域）。
17. **假设** CWD 是 `/home/user/project`，**当**用户执行 `pwd` 或 `echo hello` 时，**则**系统应该自动允许（不访问文件系统路径，无路径范围可检查）。

### 用户故事：复合命令与命令替换的结构感知判定（优先级：P1）

作为用户，我希望以循环（`for`/`while`/`until`）、分支（`if`/`case`）等 shell 控制结构包裹的、或通过命令替换（`$( )`/反引号）嵌套的**只读**命令，与直接执行的同一条只读命令一样被自动允许，以便 Explore/Plan 等只读代理批量探查代码时不因外层 shell 语法而被逐次打断；同时，被包裹/嵌套的任何破坏性命令仍须像直接执行时一样提示确认，不因藏在循环体或命令替换里而获得放行。

**验收场景**：

1. **假设** CWD 是 `/home/user/project` 且目录下存在 `a.txt`/`b.txt`，**当**用户执行 `for f in a.txt b.txt; do head -5 "$f"; done` 时，**则**系统应该自动允许（循环体内仅 `head`，只读且在安全区域内；控制结构关键字 `for`/`do`/`done` 不作为命令参与判定）。
2. **假设** CWD 是 `/home/user/project`，**当**用户执行 `for f in "$@"; do sed -n '1,10p' "$f"; done`、`while read -r line; do echo "$line"; done < input.txt`、`case "$x" in foo) grep foo docs/;; esac` 或 `if grep -q pattern docs/specs/; then echo found; fi` 时，**则**系统应该自动允许（`while`/`if`/`case` 的控制结构关键字同样不参与判定，体内每条命令只读即放行）。
3. **假设**任何目录，**当**用户执行 `for f in $(ls docs/); do echo "== $f =="; head -3 "docs/$f"; done` 时，**则**系统应该自动允许（`$(ls docs/)` 中嵌套的 `ls` 只读；`echo`/`head` 只读）。
4. **假设**任何目录，**当**用户执行 `count=$(grep -c error log.txt | tr -d ' ')` 后在同一命令内以 `echo "count=$count"` 引用该变量时，**则**系统应该自动允许（`$( )` 内 `grep`/`tr` 与后续 `echo` 均只读；赋值语句只记录变量来源，不作为命令判定）。
5. **假设**任何目录，**当**用户执行 `for f in a.txt b.txt; do rm "$f"; done` 或 `if true; then git push; fi` 时，**则**系统不得自动允许，必须提示权限确认（循环/分支体内嵌套 `rm`/`git push` 等破坏性或非只读命令，与直接执行同样判定）。
6. **假设**任何目录，**当**用户执行 `head -5 $(rm -f a.txt)` 或 `echo $(git commit -m x)` 时，**则**系统不得自动允许，必须提示权限确认（命令替换内嵌套破坏性/非只读命令，须独立判定后提示）。
7. **假设**任何目录，**当**用户执行 `cat <(echo hi)`、`diff <(sort a) <(sort b)` 或 `while read x; do echo "$x"; done >(tee out)` 时，**则**系统不得自动允许（进程替换 `<(...)`/`>(...)` 语义无法静态还原，一律须提示确认）。
8. **假设**任何目录，**当**用户执行 `for f in $(cat /etc/passwd); do echo "$f"; done` 时，**则**系统不得自动允许（`$(cat /etc/passwd)` 内 `cat` 访问安全区域外路径，路径越界规则逐命令生效）。
9. **假设**任何目录，**当**用户执行包含无法解析的语法（解析失败/超时）、或含算术展开 `$((...))`、brace 展开 `{a,b}`、`eval`、`trap` 等无法静态还原结构的命令时，**则**系统不得自动允许，必须提示确认（无法确认即不自动放行，fail-closed）。
10. **假设**任何目录，**当**用户执行 `for i in -rf /; do rm $i; done` 或循环体内以裸 `$i`/`$f`（未加引号）引用循环变量时，**则**系统不得自动允许（循环变量/命令替换捕获值在裸参数位可能被拆分或通配展开为额外参数，其值无法静态确定，相关部分不做自动放行）。
11. **假设** `permissions.allow` 包含 `Bash(node scripts*)`，**当**用户执行 `for f in $(ls scripts); do node "scripts/$f" --dry-run; done` 时，**则**系统应该自动允许（规则匹配与只读判定同样以展开后的每条叶命令为对象，`$(ls scripts)` 内 `ls` 只读自动放行、`node scripts/* --dry-run` 命中 allow 规则）。
12. **假设**任何目录，**当**用户执行 `cd /etc && for f in $(ls); do echo "$f"; done` 时，**则**系统不得自动允许（`cd /etc` 超出安全区域，既有 cd 路径规则不因外层为循环结构而改变）。

### 用户故事：MCP 工具权限（优先级：P1）

作为用户，我希望 MCP 工具受到与内置受限工具相同的权限检查，这样我就可以控制 agent 可以执行哪些外部工具。

**验收场景**：

1. **假设**调用了 MCP 工具（以 `mcp__` 为前缀），**当**没有匹配的权限规则时，**则**系统必须提示确认。
2. **假设**显示了 MCP 工具的确认提示，**当**用户选择"是，不再询问"时，**则**系统必须以 `mcp__server__tool` 格式保存持久规则。
3. **假设**存在持久规则 `mcp__server__tool`，**当** agent 调用该特定 MCP 工具时，**则**它必须立即执行而不提示。
4. **假设** `Exec` 脚本内嵌套调用某个 `mcp__` 工具（见 `docs/specs/core/exec-tool.md`），**当**没有匹配的权限规则时，**则**必须弹出与直接调用时相同的确认——规则匹配的对象是被调用的叶子工具全名，外层是 `Exec` 不改变内层工具的身份，也不构成豁免。
5. **假设**存在针对某叶子工具全名的拒绝规则，**当** `Exec` 脚本内嵌套调用该工具时，**则**该次调用必须被拒绝，且拒绝理由作为该次调用的错误回到脚本。

### 用户故事：编程式和会话特定权限（优先级：P1）

作为使用 SDK 的开发者或 CLI 上的用户，我希望提供仅应用于当前 agent 实例或会话的临时权限规则（包括允许和禁止）。这允许在不修改全局设置的情况下进行细粒度的安全控制。

**验收场景**：

1. **假设**通过 SDK 创建的 Agent 配置了 `disallowedTools: ["Bash(rm *)"]`，**当** AI 尝试运行 `rm -rf /` 时，**则**操作被拒绝，即使 `Bash` 在其他情况下被允许。
2. **假设** CLI 启动时带有 `--allowedTools "Bash(git status)"`，**当** agent 运行 `git status` 时，**则**仅在该会话中自动批准。
3. **假设** agent 配置了 `tools: ["Bash"]`（过滤）和 `disallowedTools: ["Bash(rm *)"]`（权限），**当** AI 尝试 `ls` 时，**则**被允许；**当**尝试 `rm` 时，**则**被拒绝。

### 用户故事：配置权限模式（优先级：P1）

一个经常需要为开发工作流绕过权限的开发者希望避免每次都输入 `--dangerously-skip-permissions`。他们希望设置持久化配置，使绕过权限成为其项目的默认行为。settings 的配置键为 `permissions.defaultMode`（与 Claude Code 的 settings schema 键名对齐；运行时权限上下文与 CLI `--permission-mode` 仍沿用 permission mode 命名）。

**验收场景**：

1. **假设**项目没有 `defaultMode` 设置，**当**用户运行 agent 命令时，**则**应用默认权限模式行为（受限工具需要确认）。
2. **假设** `settings.json` 包含 `"permissions": {"defaultMode": "bypassPermissions"}`，**当**用户运行 agent 命令时，**则**权限被绕过而不提示。
3. **假设** `settings.json` 包含 `"permissions": {"defaultMode": "default"}`，**当**用户运行 agent 命令时，**则**用户对受限工具被提示确认。
4. **假设** `settings.json` 包含无效的 `defaultMode` 值，**当** agent 启动时，**则**系统回退到默认权限行为并记录警告。
5. **假设**存量配置（改名前的 `settings.json`）仍使用旧键 `permissions.permissionMode`，**当** agent 启动时，**则**该键不再被识别（忽略），有效权限模式回落到默认 `default`（受限工具需确认）；存量文件须一次性迁移为 `permissions.defaultMode`（彻底改名，不做兼容读取）。

### 用户故事：从提示自动接受文件编辑（优先级：P1）

作为用户，当我被提示确认文件编辑或目录创建时，我希望能够选择自动接受当前会话中所有未来的编辑，这样我就不必逐个确认。

**验收场景**：

1. **假设** agent 处于 `default` 模式，**当** agent 尝试 `Write` 或 `mkdir` 操作时，**则**确认提示显示一个选项："是，并自动接受编辑"。
2. **假设**显示了 `Write` 或 `mkdir` 操作的确认提示，**当**用户选择"是，并自动接受编辑"时，**则**当前操作被执行，agent 的权限模式设置为 `acceptEdits`。
3. **假设** agent 的权限模式通过提示被设置为 `acceptEdits`，**当** agent 尝试后续的 `Edit` 或 `mkdir` 操作时，**则**它被执行而不提示。

### 用户故事：持久化 Bash 命令权限（优先级：P1）

作为用户，当我被提示确认 Bash 命令时，我希望能够允许该特定命令在当前项目中无需再次询问即可运行，以便我可以安全地自动化重复任务。

**验收场景**：

1. **假设** agent 处于 `default` 模式，**当** agent 尝试命令为 `ls` 的 `Bash` 操作时，**则**确认提示显示一个选项："是，并在此工作目录中不再为此命令询问"。
2. **假设**显示了 `Bash` 命令 `ls` 的确认提示，**当**用户选择"是，不再询问..."时，**则**命令被执行，`Bash(ls)` 被添加到 `.wave/settings.local.json` 的 `permissions.allow` 数组中。
3. **假设** `Bash(ls)` 在本地项目设置的 `permissions.allow` 数组中，**当** agent 尝试命令为 `ls` 的 `Bash` 操作时，**则**它被执行而不提示。

### 用户故事：安全区域内的自动文件编辑（优先级：P1）

作为用户，我希望 agent 在我的项目目录或明确允许的目录内自动应用文件编辑，而无需每次都征求我的许可，这样当我信任 agent 的更改时可以提高工作效率。

**验收场景**：

1. **假设** agent 处于 `acceptEdits` 模式且文件在安全区域内（CWD 或 `additionalDirectories`），**当** agent 尝试使用 `Edit`、`Delete` 或 `Write` 工具时，**则**操作立即执行，无需权限提示。
2. **假设** agent 处于 `acceptEdits` 模式且目录在安全区域内，**当** agent 尝试通过 `Bash` 工具使用 `mkdir` 时，**则**操作立即执行，无需权限提示。
3. **假设** 自动记忆已开启，**则** 记忆目录与用户记忆文件按安全区域放行读写，但**不构成 shell 可停留的目录**：`Bash` 把当前目录 `cd` 到记忆目录时必须复位回会话原工作目录（见 [bash-tools.md](bash-tools.md)「执行前台命令」场景 5）——它们是为免权限提示而发的内部白名单，不是用户声明的附加目录。

### 用户故事：越界安全确认（优先级：P1）

作为用户，我希望系统在修改项目或允许目录之外的任何文件之前征求我的明确许可，即使我已启用自动接受模式，以便我可以防止对我的系统进行意外或恶意更改。

**验收场景**：

1. **假设**文件位于安全区域之外，**当**系统尝试写入或编辑该文件时，**则**向用户显示确认提示，无论 `acceptEdits` 设置如何。
2. **假设**系统处于 `acceptEdits` 模式，**当**尝试越界文件操作时，**则**系统仍必须显示确认提示，而不是自动执行。

### 用户故事：附加工作目录（优先级：P1）

作为用户，我希望将工作目录之外的其他目录加入 agent 的安全区域，使这些目录中的文件操作在 `acceptEdits` 模式下自动放行、并让 agent 在系统提示词中感知这些目录的存在，从而允许 agent 安全地访问我指定的外部目录而无需反复确认。

**验收场景**：

1. **假设** `settings.json` 的 `permissions.additionalDirectories` 包含 `/data/exports`，**当** agent 启动时，**则** `/data/exports` 属于安全区域，且主 agent 系统提示词的 `# Environment` 段包含 `Additional working directories:` 及其下的目录列表。
2. **假设** 主 agent 系统提示词已列出附加目录，**当** agent 处于 `acceptEdits` 模式并尝试 `Edit`、`Delete` 或 `Write` 附加目录内的文件时，**则** 操作立即执行，无需权限提示。
3. **假设** 目录不在安全区域内，**当** agent 尝试修改该目录内的文件时，**则** 即使处于 `acceptEdits` 模式仍显示确认提示（与"越界安全确认"一致）。
4. **假设** 用户以 `wave --add-dir /data/exports` 启动 CLI，**当** agent 运行时，**则** `/data/exports` 仅在当前会话内属于安全区域并在提示词中列出，会话结束后不写入任何配置文件。
5. **假设** CLI 会话进行中，**当** 用户输入 `/add-dir /data/exports` 时，**则** `/data/exports` 在当前会话立即加入安全区域，系统提示词在下一次构建时包含该目录。
6. **假设** 用户输入 `/add-dir --remember /data/exports`，**当** 命令执行后，**则** `/data/exports` 除当前会话生效外，还被追加到 `.wave/settings.local.json` 的 `permissions.additionalDirectories`，后续会话自动加载。
7. **假设** 用户输入无参数的 `/add-dir`，**当** 命令执行时，**则** 显示用法说明及当前会话的附加目录列表。
8. **假设** agent 派生子 agent，**当** 子 agent 的系统提示词构建时，**则** `<env>` 中包含单行 `Additional working directories: /data/exports`（附加目录并集），且子 agent 的权限检查同样将附加目录视为安全区域。
9. **假设** `permissions.additionalDirectories` 的某项路径以 `~` 或 `~/` 开头（如 `~/github`），**当** 该路径被解析进安全区域时，**则** 按当前用户主目录展开为绝对路径（`~/github` → `<主目录>/github`，对齐 Claude Code），安全区域判定与系统提示词均使用展开后的路径；配置文件中保留 `~` 写法，不暴露本机用户名。
10. **假设** agent 处于确认流程且 `Write`/`Edit` 的目标文件位于安全区域之外，**当** 确认弹窗展示时（三端 GUI 或交互式 CLI），**则** 弹窗在"批准并继续"之外额外提供"允许本会话编辑 `<目录名>/`"选项（`<目录名>` 为目标文件所在目录的末级目录名）——GUI 文案为"是，且允许本会话编辑 `<目录名>/`"，CLI 文案为 `Yes, and allow all edits in <目录名>/ this session`；该选项仅在目标文件越界时出现。
11. **假设** 用户在越界确认弹窗中选择"是，且允许本会话编辑 `<目录名>/`"，**当** 操作执行后，**则** 目标文件所在目录立即加入当前会话的安全区域（仅内存，不写入任何配置文件），本会话内该目录下的后续 `Edit`/`Write` 不再触发确认，且主 agent 系统提示词在下一次构建时列出该目录。
12. **假设** 用户在越界确认弹窗中选择"批准并继续"，**当** 操作执行后，**则** 仅本次操作放行，该目录不加入安全区域，后续对该目录内文件的编辑仍显示确认弹窗。
13. **假设** `Write`/`Edit` 的目标文件位于安全区域内（例如 `default` 模式下安全区内文件的首次写入），**当** 确认弹窗展示时，**则** 不出现"是，且允许本会话编辑"选项。

### 用户故事：CLI 模式切换（优先级：P2）

作为 CLI 用户，我希望在会话期间使用键盘快捷键快速切换权限模式，以便我可以轻松地在手动控制、自动编辑、规划和绕过之间切换。

**验收场景**：

1. **假设** CLI 会话处于活动状态且为 `default` 模式，**当**用户按下 `Shift+Tab` 时，**则**权限模式更改为 `acceptEdits`。
2. **假设** CLI 处于 `acceptEdits` 模式，**当**用户按下 `Shift+Tab` 时，**则**权限模式更改为 `plan`。
3. **假设** CLI 处于 `plan` 模式，**当**用户按下 `Shift+Tab` 时，**则**权限模式更改为 `bypassPermissions`。
4. **假设** CLI 处于 `bypassPermissions` 模式，**当**用户按下 `Shift+Tab` 时，**则**权限模式更改回 `default`。

### 用户故事：GUI 端权限模式菜单快捷键（优先级：P2）

作为 GUI 用户（VS Code 插件 / JetBrains 插件 / 桌面端，共享 webview），我希望按 `Cmd+Shift+M`（macOS）/ `Ctrl+Shift+M`（Windows/Linux）打开权限模式菜单并从中选择模式，以便无需点击输入框左下角的模式按钮、也不必循环多个模式即可直接切换（对齐 Claude Code 桌面端的 `Cmd+Shift+M` 打开权限模式菜单）。

**为什么是这个优先级**：与 CLI 的 Shift+Tab 循环并列的 GUI 快捷入口；CLI 端保持 Shift+Tab 循环不变，GUI 端不再用 Shift+Tab 循环权限模式（该键恢复默认焦点移动行为）。

**验收场景**：

1. **假设** GUI webview 输入框聚焦且当前为 `default` 模式，**当**用户按下 `Cmd+Shift+M`（macOS）/ `Ctrl+Shift+M`（Windows/Linux）时，**则**打开权限模式菜单（输入框左下角下拉），展示 `default` / `acceptEdits` / `plan` / `bypassPermissions` 四个选项（与 CLI Shift+Tab 循环顺序一致），而非直接切换模式。
2. **假设**权限模式菜单已通过快捷键打开，**当**用户从菜单中选择某个模式（如 `acceptEdits`）时，**则**权限模式更改为该模式且菜单关闭，行为与点击模式按钮打开后选择完全一致。
3. **假设** GUI webview 输入框聚焦，**当**用户按下 `Shift+Tab` 时，**则**不再循环权限模式，按键落到浏览器默认行为（移动焦点）。
4. **假设**桌面端用户查看应用菜单栏，**则**「对话」菜单下存在「权限模式…」菜单项并显示对应快捷键，点击菜单项与按快捷键等效，均打开权限模式菜单。
5. **假设** JetBrains 插件 webview 聚焦（IDE 的 `Cmd/Ctrl+Shift+M` 默认绑定为"移动到匹配括号"），**当**用户按下快捷键时，**则**打开权限模式菜单且 IDE 动作不触发（组件级拦截转发到 webview）。
6. **假设** VS Code 中 webview 聚焦（VS Code 的 `Cmd/Ctrl+Shift+M` 默认绑定为"聚焦问题面板"），**当**用户按下快捷键时，**则**打开权限模式菜单且问题面板不弹出。
7. **假设**权限模式菜单展开（无论从按钮点开、快捷键打开，还是远控页手机上点开），**则**每个选项都是「图标 + 档位名」一行，**图标与文案落在同一条水平线上**（居中对齐，不按文字基线排），两者之间留出间距，不得出现图标浮在文字上方、字形贴着文字的样子。四端（桌面端 / VS Code / JetBrains / 远控页）同一套排布，差异只在字号与图标尺寸的密度档；文字档与图标档出的都是这一个菜单，形状一致。

### 用户故事：自动拒绝未批准的工具（优先级：P1）

作为用户，我希望当我处于 `dontAsk` 模式时，未经预批准的工具被自动拒绝，这样我就不会被未明确允许的工具的权限请求打断。

**验收场景**：

1. **假设**权限模式设置为 `dontAsk` 且 `Bash` 不在 `permissions.allow` 中，**当** agent 调用 `Bash` 时，**则**工具调用被立即拒绝，agent 收到"权限拒绝"错误，用户不会被提示。

### 用户故事：配置 dontAsk 模式（优先级：P2）

作为用户，我希望能够将权限模式设置为 `dontAsk`，以便我可以跨会话强制执行此行为。

**验收场景**：

1. **假设**配置文件中有 `defaultMode: "dontAsk"`，**当** agent 启动时，**则**有效权限模式为 `dontAsk`。

### ~~用户故事：Bash Heredoc 写入重定向到专用工具（优先级：P1）~~

*已移除。基于 Heredoc 的 bash 命令不再被自动拒绝。用户应依赖工具权限规则和软提示引导来鼓励使用专用的 Write/Edit 工具。*

## 关键实体

* **权限模式**：确定所需用户干预级别的配置。settings 中由 `permissions.defaultMode` 配置（对齐 Claude Code settings schema 键名），运行时权限上下文（`PermissionContext.permissionMode`）与 CLI `--permission-mode` 沿用 permission mode 命名。
* **权限规则**：定义允许或拒绝操作的字符串（如 `Bash(git *)`、`Read(**/*.env)`）。
* **简单命令**：从管道中提取的带参数的单个可执行命令。
* **智能通配符**：用 `*` 替换动态参数的启发式生成模式。
* **PermissionDecision**：权限检查的结果，扩展为包含可选的 `newPermissionMode`、`newPermissionRule` 与 `newAdditionalDirectory`（会话级地把指定目录加入安全区域）以通知系统更新其状态。`newAdditionalDirectory` 同时登记该目录下 `Edit`/`Write` 的会话级允许规则，使"允许本会话编辑"的承诺对后续编辑同样成立（`default` 模式本就对每次 `Edit`/`Write` 要求确认，仅加入安全区域不足以免除后续确认）。
* **安全区域**：允许 agent 执行文件操作而无需每次操作都经用户明确确认的文件系统路径集合。
* **附加目录**：用户可配置的路径列表，将安全区域扩展到默认工作目录之外。可通过 `settings.json` 的 `permissions.additionalDirectories`、CLI 的 `--add-dir` 启动选项、`/add-dir` 斜杠命令（会话级）或越界确认弹窗的"允许本会话编辑 `<目录名>/`"选项（会话级）加入；其中 `--add-dir` 与 `/add-dir` 为 CLI 专属入口，弹窗选项为三端 GUI 与交互式 CLI 共有（两者都不写配置文件）。路径支持相对路径（相对工作目录解析）与 `~`/`~/` 前缀（按用户主目录展开）。
* **绕过授权（会话级）**：会话创建时的有效权限模式为 `bypassPermissions` 时获得，用于在 `plan` 模式下替代绕过判定（见 `plan-mode.md`「计划模式继承会话的绕过授权」）。会话中途切换到 `bypassPermissions` 不会获得该授权；仅按用户配置自动放行的场景不受影响，常驻的 `default`/`acceptEdits`/`dontAsk` 会话行为不变。

## 边界情况

* **嵌套操作符**：如 `cmd1 && (cmd2 | cmd3)` 的管道必须递归分解。
* **结构感知解析失败**：shell 控制结构或命令替换无法被结构感知解析（语法不支持、解析超时、变量值无法静态确定等）时不得自动放行，一律回落至确认（fail-closed），防止以复杂语法绕过只读判定。
* **优先级**：拒绝规则始终优先于允许规则。
* **绕过授权不豁免的检查**：穿透权限检查的判定（`bypassPermissions` 模式，或 `plan` 模式下持有绕过授权的会话）不豁免三类拦截：实例与配置的 deny 规则、worktree 内对主仓库的越界写保护、以及需要用户交互的工具（`AskUserQuestion`、`ExitPlanMode`——后者即计划批准，离开计划模式始终是用户决定）。
* **敏感命令**：如 `rm` 等命令被列入智能通配符建议的黑名单，以防止意外的广泛权限。
* **转义字符**：`echo "&&"` 必须被视为单个命令，不得拆分。
* **缺少 `.wave` 目录**：如果用户选择持久化 Bash 选项且 `.wave` 不存在，系统应该创建它。
* **格式错误的 `settings.json`**：如果设置文件格式错误，系统应该优雅地处理。
* **重复规则**：如果规则已存在，再次选择该选项不应创建重复项。
* **受限与不受限工具**：不受限工具（不在 `RESTRICTED_TOOLS` 中的工具）仍应被自动允许，即使在 `dontAsk` 模式下。
* **符号链接**：系统应该解析真实路径并对照安全区域检查，以防止通过符号链接绕过。
* **嵌套目录**：列出目录或其子目录内的任何文件都应被视为安全。
* **重复附加目录**：同一目录通过配置、`--add-dir`、`/add-dir` 与越界确认弹窗选项等多个入口重复加入时，系统提示词列表与 `--remember` 持久化均应去重，不产生重复项。
* **`Exec` 嵌套调用与规则匹配**：权限规则按**叶子 MCP 工具全名**（`mcp__server__tool`）匹配，不按外层 `Exec` 匹配；拒绝 `Exec` 只影响 `Exec` 自身是否声明与可调用，不得连带拒绝其池中的 MCP 工具（此时它们退回扁平声明，安全语义不变）。
