Docs / Autopilot / Modes and permissions
Modes and permissions
How the plan, edit and auto modes decide which tool calls run without asking, and how to set permission rules for headless runs.
Modes are off by default
Plan, edit and auto modes are behind a setting that is off by default. With modes off, every turn runs in auto: tool calls run without per-call prompts, and the mode controls (Shift+Tab, /mode, the mode badge) are hidden.
Turn modes on:
/settings modes on
This starts you in edit. Turn them off again with /settings modes off. The setting is stored as modesEnabled in the config file.
The three modes
| Mode | What runs without asking | Use it for |
|---|---|---|
plan |
Read-only tools only (read, glob, grep, web search, read-only GitHub, browser fetch). Writes, edits, mutating shell commands, MCP tools and LSP renames are blocked. | Research and planning before you change anything |
edit |
Read-only tools. Each mutating call asks for approval. | Day-to-day work where you review each change |
auto |
Everything. | Trusted tasks |
Switch mode
| Action | Effect |
|---|---|
Shift+Tab |
Cycle edit, plan, auto |
/mode edit, /mode plan, /mode auto |
Switch directly |
/plan, /edit, /auto |
Short forms |
Plan mode and shell commands
In plan mode, the bash tool only runs commands it can classify as pure reads. Plain pipes and && are allowed. Redirection, ;, ||, a lone &, command substitution, variable expansion and backslash escapes are refused. Each command must be on an allowlist (for example ls, cat, grep, rg, wc, diff and jq) and must not use an option that runs a program or writes a file. The allowlist is in src/mode.ts.
Protected reads and local network access
In plan and edit:
- Reading a credentials file asks first. This covers
~/.ssh,~/.aws, Autopilot's own config with your provider keys,.envand/proc/*/environ. A prompt injection then cannot read a key and send it out withweb_fetch. web_fetchandbrowser_fetchask before reaching a local or private network address:localhost,127.0.0.1,10.x,192.168.x, and cloud metadata at169.254.169.254, including names and redirects that resolve there. Approval is per origin.
The permission prompt
When a call needs approval, a prompt appears in the UI:
- Navigate with
↑/↓orj/k, or choose directly withAlt+1,Alt+2,Alt+3. - Denying a call opens inline feedback, so you can tell the agent what to do instead.
- When the agent launches several subagents in one step, one prompt covers the whole batch.
Loop guardrails
If every tool call in a turn is blocked, the agent is given a recovery instruction. If that happens again, the turn ends with a tool-free summary instead of retrying. This stops a turn from spending tokens on calls that will never run.
Permission rules for headless runs
In print mode (-p) there is no one to answer a prompt. A call that would ask is denied, unless one of these decides it first:
--dangerously-allow-allapproves every call.- A rule in the
permissionsconfig key. - A
PermissionRequesthook.
Rules are keyed by tool name. Each maps a pattern to allow, deny or ask. Patterns match the target path for file tools, and the command string for bash.
{
"permissions": {
"bash": { "npm test": "allow", "**": "ask" },
"write": { "**": "deny" },
"edit": { "~/trusted/**": "allow", "**": "ask" }
}
}
- The longest (most specific) matching pattern wins.
- With no matching rule, the result is
ask, which is denied in print mode. - A pattern matches a target that starts with it, or that matches it as a glob.
- For
bash, anallowprefix must end at a word boundary (npm testallowsnpm test -- --watch, notnpm testify), and the rest of the command may contain no shell control syntax such as;,&,|,`,$(or>. A pattern made only of wildcards (**) still allows every command. denyandaskrules also match each command inside a compound command, socd x && rm -rf /matches a rule forrm -rf.
The evaluator is src/permissions-evaluator.ts.
Pushing to the default branch
By default the bash tool blocks git push to the repository's default branch and directs the model to open a pull request instead. Set "allowDirectPush": true to allow it. preferPullRequests (default true) adds the same preference to the system prompt.
In the SDK
createAgentSession takes a permissionHandler that decides each request, and session.setMode("plan" | "edit" | "auto") changes the mode. A single prompt can override it with prompt(text, { mode }). See SDK and RPC.