Level 1 — anything untrusted
The default. Use it when you are exploring, running a new skill, or pointing the agent at code you did not write.
An agent that can write files, run shell commands, and reach the internet needs a place where you say yes. In Atomic Agent that place is the approval gate: before a dangerous tool runs, the runtime classifies what it is about to do and either lets it through or stops and asks you.
How much it asks is one number. agent.approvalLevel runs from 1 (ask about everything) to 5 (ask about nothing), and each step up hands the agent a broader class of actions to take on its own.
The default is level 1. Every gated action prompts you before it happens. In the TUI you can also shift the stance for one session with the coding mode control.
| Level | Name | Runs without asking |
|---|---|---|
| 1 | paranoid | Nothing. Every gated action prompts. |
| 2 | workspace | File writes, edits, and patches strictly inside the session working directory. |
| 3 | home | Adds file writes anywhere under your home directory, moves to Trash, archive extraction, and HTTP requests (only if http.approvalMode asks for them, see below). |
| 4 | operator | Adds guarded shell commands, skill scripts, process kills, network git (push, pull, fetch, clone, remote), GitHub publishing (PRs, issues, comments), and Fusion fan-outs. |
| 5 | full trust | Everything, including browser navigation to non-web URLs, writes to the agent’s own trust config, sending e-mail, reads outside the working directory, and anything the runtime could not categorise. |
A short allowlist of harmless shell probes (pwd, whoami, date, which <cmd>, node --version-style version probes for common runtimes, a literal echo) and a few read-only CLI rules run without a prompt even at level 1.
The ladder is cumulative by construction. The gate asks level >= threshold, so a category that is silent at level 3 is silent at 4 and 5 too — there is no level where something starts asking again.
// <stateDir>/config.json{ "agent": { "approvalLevel": 2 }}Every call site that asks for approval names one of fifteen categories. The category decides both the level at which it goes quiet and the label you see in the prompt.
| Category | Prompt label | Auto-approves from |
|---|---|---|
fs_write_workspace | file write · workspace | 2 |
fs_write_home | file write · home | 3 |
fs_trash | move to Trash | 3 |
http | HTTP request | 3 |
shell | shell command | 4 |
script | skill script | 4 |
proc_kill | process kill | 4 |
publish | publish · GitHub | 4 |
git_remote | git · remote | 4 |
fusion_fanout | fusion · fan-out | 4 |
browser_nonweb | browser · non-web URL | 5 |
trust_config | agent trust config | 5 |
email | e-mail send | 5 |
fs_read_outside | read outside the working directory | 5 |
other | uncategorised | 5 |
other is the deliberate fallback for anything a call site cannot place precisely. Because it only goes silent at 5, an uncategorised action keeps asking on every level below full trust — the conservative default.
http only comes into play when http.approvalMode asks for it. The default is "never": os.http.request runs without a prompt at every level. Set "writes" to ask before POST requests, or "always" to ask before every request; the http category then goes quiet from level 3. os.web.fetch and os.web.search are read tools and are never gated.
By default (agent.readScope: "working-dir") the agent reads freely inside the session working directory and inside paths you named in the conversation. A read, or a shell command that names a path, anywhere else stops and asks under fs_read_outside. Answering y opens that directory (a file’s parent folder) for the rest of the session; s allows reads anywhere for the session. Level 5 never asks. Set agent.readScope: "unrestricted" to turn the scope off.
When the gate stops, the CLI prompt on stderr is not a yes/no. It takes four:
| Answer | Effect |
|---|---|
y | Approve this one call. |
s | Approve, and allow this whole category for the rest of the session. |
a | Approve, and allow this shell command shape for the rest of the session. |
N | Deny. |
Only y and yes count as approval. Anything else — a typo, an empty line, maybe — is a refusal. Denial is the failure mode, which is what you want from a gate.
» approval required for tool: os.fs.write kind: file write · home reason: write 2.1 KB to ~/notes/plan.md affects: /Users/you/notes/plan.md approve? [y = approve once, s = allow this kind this session, N = deny]s and a appear only when the request is grantable. a needs a shell command shape to generalise from, so it shows up on shell requests and not on file writes. It is also not offered for opaque interpreter calls such as bash -c, where the binary name hides what runs. And trust_config, email and fusion_fanout are never grantable: those requests only offer y/N, so each e-mail and each Fusion fan-out is its own decision. The prompt says so:
(trust-config writes are never granted for the session)(The CLI currently prints this trust-config line for every non-grantable request, including e-mail and fan-out.) For trust_config this is the grant-side half of the same invariant as the level pin. A broad category grant earlier in the session must not later cover a write to the file that holds the ladder.
Every interactive entry point (run, tui, serve) accepts --no-approval, which forces level 5 for that process.
atomic-agent run --no-approvalThe persisted agent.approvalLevel is the baseline and the flag overrides it for the process. run, tui, and serve all resolve the boot level the same way, so the level you set in config applies consistently across entry points.
The TUI has one approval control: the coding mode chip in the composer. Open it with ctrl+g M, click the chip, or type /mode <name>.
| Mode | Effect |
|---|---|
default | Approvals follow agent.approvalLevel from config.json. |
plan | Read-only: the agent reads and proposes, and every tool that would change something is refused. |
auto | Raises the level to at least 2 (never lowers it): file writes inside the working directory stop asking, everything else still asks. |
bypass permissions | Level 5 for the rest of this session. Hardline shell-guard rules still block. |
The mode applies to the current session only and is never written to config.json. Switching back to default restores the level you configured. To change the persisted baseline, run atomic-agent config set agent.approvalLevel 3 (or edit config.json).
Atomic Agent lets the model emit several tool calls in one step, executed by resource class. Approval-gated tools are normally excluded from that: they carry the virtual approval_gated class and must run one per step.
If the model emits one anyway, the runtime does not fail the step. It trims the batch to the first approval-gated call, executes that one, drops the rest, and tells the model what happened in the next step’s prompt so it can re-emit the dropped calls one at a time.
When nothing in the batch could ask (every call is auto-approved at the current level or covered by an s grant), the batch is not trimmed and the gated calls run one after another in the order the model emitted them. In practice file writes only qualify at level 5 (for example under --no-approval), because a write could land on the agent’s own config. fusion.delegate always runs alone.
These are the approval-gated tools:
| Tool | What it does |
|---|---|
os.fs.write | Write a file |
os.fs.edit | Edit a file in place |
os.fs.patch | Apply a patch |
os.fs.restore | Put back a file’s previous content |
os.fs.trash | Move to Trash |
os.fs.archive.extract | Extract an archive |
os.shell.run | Run a shell command |
os.proc.kill | Kill a process |
os.http.request | Issue an HTTP request |
skill.run_script | Run a skill script |
os.git.checkout, os.git.commit, os.git.init, os.git.add | Local git writes |
os.git.remote, os.git.fetch, os.git.pull, os.git.push, os.git.clone | Network git |
github.pr.create, github.issue.create, github.issue.comment | Publish on GitHub |
os.email.send | Send an e-mail |
fusion.delegate | Fan work out to Fusion workers |
verify.run | Run a check in a throwaway copy of the working directory |
Tools from MCP servers at the default trust level ask as other, unless the tool advertises readOnlyHint.
Read-only counterparts (os.fs.read, os.fs.grep, os.web.fetch, os.web.search, os.git.status|log|diff|show|blame|branch, the memory.* recalls) are pure_read and batch freely.
Before config v37 the setting was a boolean, agent.approvalRequired. It is migrated automatically:
| Legacy value | Becomes |
|---|---|
approvalRequired: true | approvalLevel: 1 |
approvalRequired: false | approvalLevel: 5 |
The migration runs when the config file is read, and the legacy key is removed from disk afterwards. If a file somehow carries both, agent.approvalLevel wins — an explicit level is never overridden by a stale boolean.
Level 1 — anything untrusted
The default. Use it when you are exploring, running a new skill, or pointing the agent at code you did not write.
Level 2 — focused project work
The agent edits freely inside the working directory but still asks before touching anything outside it or running shell.
Level 3 — research and fetching
Adds home-directory writes, Trash and archive extraction (and HTTP, if http.approvalMode asks). Good for tasks that read the web and take notes; shell still stops for you.
Level 4-5 — trusted automation
Shell and skill scripts run unattended. Only for workloads and inputs you fully control, ideally in a sandbox.