# Permissions and Confirmations

The two permission modes, which entry point uses which, and how confirmation prompts are routed.

## Permission mode

| Mode | Behavior |
|---|---|
| `single-confirm` | Each non-ReadOnly tool call requires user confirmation (the TUI default) |
| `always-allow` | Tools auto-execute; the LLM is instructed to invoke `ask_user` first for seven categories of truly irreversible operations |

The seven irreversible categories that still require explicit `ask_user` under `always-allow`:

1. Filesystem — `rm -rf` / `rm -r`, deleting directories or existing files not produced by this task
2. Database — `DROP DATABASE` / `DROP TABLE` / `TRUNCATE`, `DELETE` / `UPDATE` without `WHERE`, any production DSN
3. Git — `reset --hard`, `push --force` to main/master, deleting shared branches, `clean -fdx`
4. System — `chmod 777` / `chown -R`, edits under `/etc` / `/usr` / `/System`, launchctl / systemd changes, sudo escalation
5. Overwrite — an unread non-empty existing file, `.env` / credentials / lock files / `.git/index`
6. Cloud & infra — `gcloud` / `aws` / `kubectl delete`, `terraform destroy`
7. Process — `shutdown` / `reboot`, `kill -9` on system service PIDs

Ordinary writes (`edit_file`, build and test commands, `git status` / `add` / `commit`, read-only shell) proceed directly. The gate is enforced by prompt (`configs/prompts/system_prompt/inject/permission/always_allow.md`, injected each turn), not by hardcoded Go-side filters.

## Mode by entry point

The active permission mode (`single-confirm` vs `always-allow`) is decided by entry point:

| Entry | Mode |
|---|---|
| TUI | `single-confirm`, toggled per session with `Shift+Tab` |
| `POST /v1/send` | `single-confirm`; confirms are answered through the web dashboard. The `allow_all` request field was removed |
| `POST /v1/chat/completions` | `always-allow` |
| Telegram | `single-confirm` (confirm gate uses a Telegram inline-keyboard select) |
| Discord | `single-confirm` (confirm gate uses a Discord select menu) |
| Resumed pending task | Keeps the mode stored with the task |
| Subagent | Inherits parent ctx |

Since v1.2.0 the mode is no longer part of the system prompt: it is rendered as a `## Permission Mode` section in the per-turn context, a user message placed before the user's input, so toggling it mid-session does not reset the prompt cache. Commands on the read-only list skip the gate in every mode.

## Auto-approval rules

A tool that carries a `mode` is gated by it: `list` / `read` / `search` are treated as read-only and skip confirmation, while `remove` / `restore` always confirm even on an otherwise auto-approved tool.

Beyond that, a call skips the prompt when its tool is registered `AlwaysAllow`, an `http_request` uses `GET`, or a `run_command` argv matches the read-only list; `open_file` on a URL and `read_files` on a sensitive file always ask. Every other call is checked against the allow list (`internal/agents/exec/allow`):

| Item | Detail |
|---|---|
| Files | `~/.config/agenvoy/allow_tool` (global) and `<workDir>/.agenvoy/allow_list` (project), one entry per line, `#` starts a comment; both are loaded and unioned |
| Entry | `name` approves every call of that tool; `name(argGlob)` approves only calls whose subject matches. The subject is the space-joined argv for `run_command`, otherwise the `path`, `url` or `target` argument, else the raw JSON arguments |
| Glob | Used for both the name and `argGlob`, anchored to the whole string: `*` matches within one `/` segment, `**` crosses `/`, `?` matches one non-`/` character, and `\*`, `\?`, `\\` are literal |
| Remember | Approving a prompt with remember writes `name(<subject>)` to the project `allow_list`, escaping `*`, `?` and `\` in the subject so the entry matches only that exact call |
| MCP | `/mcp` → server → tools, or `POST /v1/allowlist` with `{tool: {prefix, entries}}`, replaces every global entry under `mcp__<server>__` with the given set; each entry must start with that prefix, and including `prefix*` collapses the set to a whole-server grant |
| Skills | `~/.config/agenvoy/allow_skill` and `<workDir>/.agenvoy/allow_skill` list Skill names; a run whose active Skill is listed executes as allow-all |

## Origin-routed confirmations

Every interactive request carries an origin prefix — `cli-`, `chat-`, `tg-`, or `dc-`. CLI confirmations are consumed only by the TUI — since v1.2.0 they travel over the daemon socket to the TUI window that started the run, not to every open TUI — web requests by the web confirmation stream, and Telegram or Discord requests by their matching channel listeners. A confirmation from a channel other than the TUI that is left unanswered for five minutes is skipped and the task is kept as pending (since v1.1.1 TUI confirmations wait without a time limit), so one channel can neither intercept nor indefinitely hold another channel's prompt.
