bonsai

Autonomy and permissions

bonsai separates what the agent may do without asking (autonomy + permission rules, this page) from what a spawned command can physically do (sandbox) and which persona and tools are active (agents and modes). This page covers command risk classification, the autonomy ladder, and the permission-rule system.

Command risk classification

Every shell command is classified into an ordered risk tier before it runs. Classification is deliberately concentrated in one auditable file (src/tool/risk.rs):

Tier Meaning Examples
read-only Reversible, no side effects beyond reading ls -la, git status, git diff, git branch (listing), gh --version
low Reversible, in-project, low blast radius cargo test, npm run build, git add <paths>, git stash, branch/tag creation, git merge --abort
medium Mutating but recoverable cargo build --release, make, git commit, git fetch/pull/merge/rebase, docker
high Hard to undo or wide blast radius rm file.txt, git push, npm install, curl https://…, gh pr create
destructive Always-ask floor; catastrophic or irreversible rm -rf build/, git push --force, git reset --hard, git checkout <path> (discards edits), curl … \| sh

How classification works:

Autonomy levels

The autonomy level sets the ceiling of what auto-runs; everything above the ceiling prompts. Set it with /autonomy <level> (aliases: default→ask, auto/accept→auto-accept), cycle the confined levels with Alt+M, or use /yolo as a shortcut for the top (/yolo on→yolo, /yolo off→ask, bare /yolo toggles).

Level Auto-runs up to Still prompts Guardrails
ask nothing everything risky on
conservative read-only low and above on
balanced (default) medium high, destructive on
auto-accept high destructive on
yolo everything nothing off

Three guardrails persist at every level except yolo:

Two deliberate asymmetries:

The status bar shows the level: quiet at ask, amber as it rises, red at yolo. Headless runs default to ask; raise explicitly with --autonomy, --yolo, or BONSAI_AUTONOMY (see Headless mode).

Permission rules

When an action prompts, you choose:

Key Choice Effect
A / Enter Allow once no rule recorded
S / M Always for this session in-memory rule, lost on restart
P Always for this project persisted allow rule, survives restarts
D / Esc Deny once the action fails; no rule recorded
N Never for this project persisted deny rule; refused automatically next time, no re-prompt

N (“Never”) is the mirror of P: where P remembers a “yes”, N remembers a “no”, so a request you keep rejecting stops asking. It is offered on every prompt that offers P (bash, domain, MCP, hook) but never on the sandbox-escape prompt — escaping the OS sandbox is the enforcement floor and is never persisted. When a command needs both ordinary permission and a sandbox escape, one warning modal covers both decisions using the narrower once/session scopes. Lift a Never rule the same way as any other: /permissions remove <id>.

Rules are glob patterns in separate namespaces so a bash pattern can never match a web domain or an MCP tool:

Kind Matches Prompt
bash each env-stripped command segment the standard permission prompt
domain a host (WebFetch/WebSearch) web-domain prompt (records redirect hops)
mcp dotted ids like mcp.github.* extension-tool prompt
hook hook.<name>:<hash> trust records one-time hook trust prompt
workspace_trust the per-project trust record the trust question

Evaluation order, first match wins:

  1. Hard-deny floor — compiled rules no user rule can override: rm -rf /, rm -rf ~, sudo *, chmod 777 *, dd *, mkfs *, and curl|wget … | sh in spaced and spaceless forms. These deny outright, no prompt possible.
  2. Session rules (newest first).
  3. Persisted rules — project scope first, then global.
  4. Built-in defaults — allow common read-only commands; explicitly ask on rm *, git push/commit/reset/… *, installs, cargo build/run *, make *, docker*.
  5. Fallback: ask.

For multi-segment commands the verdict is the most restrictive across segments: any deny → deny; else any ask → ask; else allow.

Two floors bound even an explicit allow:

Managing rules

Typed action plans (non-bash effects)

File mutations, MCP calls, web fetches, and other non-shell effects don’t go through command classification — each builds a typed action plan naming its effects (workspace write, delete, truncate, multi-file write, protected path, network, code execution, irreversible, …), each effect mapped to a risk tier. The same verdict combiner then applies: explicit deny → deny; remembered allow → auto-approve (destructive floor permitting); otherwise the autonomy ceiling decides between auto-approval and a prompt. Every decision — allowed, denied, prompted, bypassed — is recorded in the authorization ledger and summarized in the completion report.

Headless behavior

Headless runs cannot answer prompts: any action that would prompt is denied with a message pointing at --autonomy, BONSAI_AUTONOMY, or a project allow rule, and the question tool fails instead of blocking. Sandbox escapes remain prompt-gated in headless mode at every autonomy level; the interactive safe-retry exception never applies.

Where this lives in the code

Concern Location
Risk tiers & classifier src/tool/risk.rs
Command tokenizing/segmentation src/tool/bash/command.rs
Bash gate src/tool/bash/policy.rs
Typed action plans & verdicts src/tool/action_policy.rs
Permission rules & namespaces src/permissions.rs
Autonomy holder src/yolo.rs
Prompt plumbing src/interaction.rs