Autonomy and approvals
Autonomy and approvals
Holaryn Agent never assumes it may act on your behalf. Every tool call is classified by its consequence, resolved against your chosen autonomy posture, and — when it matters — held for your explicit yes. This page explains the model, the postures, unattended behavior, policy overrides, and dry-run mode.
The two axes
Autonomy is not one dial. Holaryn separates two independent questions:
- Control — does the action wait for your yes before it runs?
- Awareness — are you told that it happened?
Crossing the two gives the four autonomy levels a tool call can resolve to:
| Level | Waits for yes? | Tells you? |
|---|---|---|
| Autonomous (silent) | No | No — trivial, reversible work only |
| Autonomous + notify | No | Yes — do it, then report |
| Ask first | Yes — the run suspends until you answer | Yes |
| Never unattended | Refused in a scheduled run; held for a foreground session | Yes |
An unanswered ask is never auto-approved and never auto-failed — the run simply waits.
Consequences
Every tool carries a declared consequence: whether the action is reversible, and a category (fs.read, fs.write, shell, script, memory.write, mcp.*, skill.*, command.script, command.app, and so on). The approval policy resolves a consequence plus the call's arguments to one of the four levels. The default rule is simple:
- Reversible → act (autonomous + notify).
- Irreversible → ask first.
Postures
The posture is the run-wide stance that decides how consequences resolve. Four postures exist:
| Posture | Behavior |
|---|---|
ask |
Everything consequential waits for you. |
selective (default) |
Reversible actions proceed with notification; irreversible actions ask. |
allow-all |
Acts on everything except a residual of hard-floor categories (shell, script, command.script, command.app by default — configurable via HOLARYN_ALLOW_ALL_RESIDUAL), which still ask. |
unrestricted |
Acts on everything; only external-app launches (command.app) still keep a human in the loop. |
Both allow-all and unrestricted are gated behind explicit confirmation ceremonies in the UI — you cannot drift into them.
Set the posture:
- CLI:
holaryn run --autonomy ask|selective|allow-all|unrestricted, or the shorthand flags--allow-all yes/--unrestricted yes. - Web chat: the mode picker in the composer, or type
/mode ask|selective|allow-all|unrestrictedin the chat. The mode change applies to the live session immediately. See slash-commands.md.
/mode is a control-plane command: only surfaces that grant the posture capability offer it (the web chat does; messaging channels and the scheduler refuse it by construction, and the CLI REPL bakes its posture in at start).
The approval dialog
When a live Chat or Code run asks first, a focus-trapped dialog moves focus to its title so you
review context before acting. It names the tool, affected resource, one-call scope, consequence,
reversibility, request time, provenance, trust/sensitivity, request identity, summarized fields,
and exact arguments (file edits show the exact diff), then offers:
- Allow once — permits only this named call.
- Deny once — refuses only this call; the run continues and the agent is told. Pressing Esc is the same explicit deny — it is announced, never a silent dismiss.
- More options, a menu with the scoped decisions:
- Approve and don't ask again for {tool} (this session) — further calls to that tool run with notification instead of asking, until the chat session ends. The grant is never persisted, and the hard-floor categories (shell, scripts) still outrank it.
- Approve all actions for this session — approves and switches the session to
allow-allin the same gesture (the same switch as the arming ceremony; shell and scripts still require approval). - Deny and terminate the run — refuses the call and stops the in-flight run cleanly. The transcript notes "run terminated by the operator" and the chat is immediately ready for your next message.
Every decision is announced to screen readers the moment it lands ("Approved {tool}", "Denied {tool}", "Session switched to allow-all", "Run terminated").
Unattended and scheduled runs
Nothing auto-proceeds in an unattended run:
- A job scheduled with
allow-allorunrestrictedis downgraded to ask-first at dispatch (with a visible warning), so consequential actions suspend instead of running unwatched. The one exception is dry-run jobs — staging is already the safe mode. - When a gated action fires unattended, the job's
--on-gatedsetting decides:holdparks the approval and suspends the run;canceldenies it. Either way the decision is yours — a suspended run never times out into approval and never silently fails. - Parked approvals, operator questions, and post-restart recovery decisions all land in the
Approvals workspace (Ctrl+0by default), which shows a pending count from anywhere. Answer
from there, or from the CLI withholaryn questionandholaryn resume.
See scheduler-and-automation.md for how jobs are defined.
Policy profiles: per-category and per-tool overrides
For routine automations, a hand-editable TOML policy profile pre-authorizes (or tightens) specific categories and tools instead of flipping one big switch. The file uses four level aliases: silent, notify, ask, never.
[selective.categories]
"fs.write" = "notify" # let the selective posture write files without asking
"team.message" = "ask"
[selective.tools]
"run_shell" = "never" # this tool never runs unattended
[allow-all.tools]
"generate_image" = "silent"
[selective.categories]and[selective.tools]refine theselectiveposture.[allow-all.tools]refinesallow-allper tool. There is deliberately noallow-all.categoriestable — the allow-all residual is controlled only byHOLARYN_ALLOW_ALL_RESIDUAL, and the residual is a hard floor a profile cannot weaken.
The active profile is resolved in this order: the --policy <file.toml> flag (on holaryn run and holaryn schedule) > the HOLARYN_POLICY_FILE environment variable > ~/.holaryn/policy.toml if it exists. Inspect the active profile with holaryn policy show, or edit rules graphically in Settings → Autonomy & Permissions.
Threshold rules ("purchases under $20 allowed, ask above"; "internal recipients silent, external ask") are also supported by the policy engine for categories that expose a numeric or recipient field.
Dry-run: propose a plan, approve in batch
Dry-run mode is how a new automation earns trust. With dry-run on, irreversible tool calls are staged into a reviewable plan instead of executing; reversible actions (reads, recalls) still run so the agent can actually plan.
Turn it on:
- CLI:
holaryn run --dry-run "reorganize my downloads"— orthogonal to the posture flags. - Web: the Dry run checkbox in the composer tray, or
/dryrun on|offin the chat. - Scheduled jobs: a dry-run flag on the job stages the run's consequential actions for later review. In this one case the unattended allow-all downgrade is skipped — previewing what allow-all would do unattended is exactly the point.
Review and execute:
- Web: when a dry-run task finishes with staged actions, a plan card appears above the composer listing every action with its consequence category. Approve & execute runs exactly the recorded calls, in order — the task is never re-run. Discard drops the plan.
- CLI (works for web- and scheduler-staged plans too):
holaryn plan list # reviewable plans
holaryn plan show <plan_id> # per-action tool, args, category, state
holaryn plan approve <plan_id> # execute the remaining staged actions, in order
holaryn plan discard <plan_id>
Plans are durable (staged-plans.sqlite3 in the state directory). Execution stops at the first failure; the remaining actions stay staged, so re-approving retries only what never ran. Two honest caveats shown in the review surfaces: arguments were computed against world state at dry-run time and may be stale, and batch approval deliberately replaces the per-call gates — which is why every call's category (including shell) is shown before you approve.
Related pages
- slash-commands.md —
/mode,/dryrun, and gated script commands - scheduler-and-automation.md — unattended jobs and the Approvals inbox
- tools-and-mcp.md — the tools these consequences belong to
- settings.md — the Autonomy & Permissions page