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, gh --version
low Reversible, in-project, low blast radius cargo test, npm run build
medium Mutating but recoverable cargo build --release, make, git commit, 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, 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 rule, survives restarts
D / Esc Deny the action fails

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-escape prompts are denied in headless mode at every autonomy level.

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