# 權限與確認

兩種權限模式、各入口使用哪一種，以及確認提示如何路由。

## Permission mode

| Mode | 行為 |
|---|---|
| `single-confirm` | 每個非 ReadOnly 工具呼叫都需使用者確認（TUI 預設值） |
| `always-allow` | 工具自動執行；LLM 被指示對七類真正不可逆的操作先呼叫 `ask_user` |

在 `always-allow` 下仍需明確 `ask_user` 的七類不可逆操作：

1. 檔案系統 —— `rm -rf` / `rm -r`、刪除目錄或非本次任務產生的既有檔案
2. 資料庫 —— `DROP DATABASE` / `DROP TABLE` / `TRUNCATE`、無 `WHERE` 的 `DELETE` / `UPDATE`、任何正式環境 DSN
3. Git —— `reset --hard`、對 main/master 的 `push --force`、刪除共用分支、`clean -fdx`
4. 系統 —— `chmod 777` / `chown -R`、`/etc` / `/usr` / `/System` 底下的修改、launchctl / systemd 變更、sudo 提權
5. 覆寫 —— 未讀取過的非空既有檔案、`.env` / 憑證 / lock 檔 / `.git/index`
6. 雲端與基礎設施 —— `gcloud` / `aws` / `kubectl delete`、`terraform destroy`
7. 行程 —— `shutdown` / `reboot`、對系統服務 PID 執行 `kill -9`

一般寫入（`edit_file`、build 與測試指令、`git status` / `add` / `commit`、唯讀 shell）直接執行。此閘門由 prompt（`configs/prompts/system_prompt/inject/permission/always_allow.md`，每輪注入）強制，而非硬編碼的 Go 端 filter。

## 各入口的模式

生效的權限模式（`single-confirm` 或 `always-allow`）由進入點決定：

| 進入點 | 模式 |
|---|---|
| TUI | `single-confirm`，可用 `Shift+Tab` 逐 session 切換 |
| `POST /v1/send` | `single-confirm`；確認由 web 儀表板回應。請求欄位 `allow_all` 已移除 |
| `POST /v1/chat/completions` | `always-allow` |
| Telegram | `single-confirm`（confirm gate 使用 Telegram inline-keyboard 選單） |
| Discord | `single-confirm`（confirm gate 使用 Discord select menu） |
| 續跑的待處理任務 | 沿用該任務儲存的模式 |
| Subagent | 繼承母 ctx |

自 v1.2.0 起模式不再寫進 system prompt：它以 `## Permission Mode` 段落渲染進每輪的 turn context（放在使用者輸入之前的一則 user 訊息），session 中途切換不會讓 prompt cache 失效。位於唯讀指令清單的指令在任何模式下都跳過 gate。

## 自動核准規則

帶 `mode` 的工具由 mode 決定閘門：`list` / `read` / `search` 視為唯讀並跳過確認，而 `remove` / `restore` 即使在已自動核准的工具上仍一律確認。

此外，工具註冊為 `AlwaysAllow`、`http_request` 使用 `GET`，或 `run_command` 的 argv 命中唯讀清單時跳過確認；`open_file` 開 URL 與 `read_files` 讀敏感檔案一律確認。其餘呼叫對照 allow list（`internal/agents/exec/allow`）：

| 項目 | 說明 |
|---|---|
| 檔案 | `~/.config/agenvoy/allow_tool`（全域）與 `<workDir>/.agenvoy/allow_list`（專案），一行一條，`#` 開頭為註解；兩者都載入並取聯集 |
| 條目 | `name` 核准該工具的所有呼叫；`name(argGlob)` 只核准主體相符的呼叫。主體在 `run_command` 是以空白串接的 argv，其他工具取 `path`、`url` 或 `target` 參數，都沒有則為原始 JSON 參數 |
| Glob | 同時用於名稱與 `argGlob`，須比對整個字串：`*` 只在單一 `/` 區段內匹配、`**` 可跨越 `/`、`?` 匹配一個非 `/` 字元，`\*`、`\?`、`\\` 為字面字元 |
| 記住 | 在確認時選擇記住，會把 `name(<主體>)` 寫進專案的 `allow_list`，主體中的 `*`、`?`、`\` 會被跳脫，因此只匹配完全相同的呼叫 |
| MCP | `/mcp` → server → tools，或 `POST /v1/allowlist` 帶 `{tool: {prefix, entries}}`，會把全域清單中 `mcp__<server>__` 開頭的條目整組換成給定集合；每個條目都必須以該前綴開頭，含 `prefix*` 時整組收斂為整個 server 的授權 |
| Skill | `~/.config/agenvoy/allow_skill` 與 `<workDir>/.agenvoy/allow_skill` 列出 Skill 名稱；作用中 Skill 在清單內的執行以 allow-all 進行 |

## 依來源路由的確認

每個互動請求都帶有來源前綴——`cli-`、`chat-`、`tg-` 或 `dc-`。CLI 的確認只由 TUI 消費——自 v1.2.0 起經 daemon socket 只送到發起該次執行的 TUI 視窗，而非所有開著的 TUI——web 請求由 web 確認串流消費，Telegram 或 Discord 請求由對應的通道 listener 消費。TUI 以外通道的確認 5 分鐘未回覆即視為 skipped，任務保留為待決（v1.1.1 起 TUI 的確認不設時限），因此一個通道既無法攔截、也無法無限期占住另一個通道的提示。
