Permissions

The permission system controls which tools agents can use and under what conditions. It uses a layered rule evaluation model with project-level and agent-level overrides.

Permission Rules

A permission rule is a string in the format "ToolName(specifier)":

Bash(npm run *)     — Allow any npm run command
Read(./.env)        — Match reading .env file
WebFetch(domain:*.example.com) — Match fetching from example.com subdomains
Bash(rm -rf *)      — Match rm -rf commands

Rule Format

ComponentDescription
Tool nameCase-insensitive tool identifier (e.g., Bash, Read, Write)
SpecifierOptional pattern inside parentheses — glob/prefix matching for commands and paths

Special specifier prefixes:

  • domain: — For WebFetch, matches against the URL's host using glob patterns
  • No prefix — Matches against the tool's primary input (command for Bash, path for Read/Write)

If no specifier is provided (e.g., just "Bash"), the rule matches all invocations of that tool.

PermissionSet

A PermissionSet contains three ordered lists of rules:

{
  "allow": ["Bash(npm run *)", "Read(*)"],
  "ask": ["Bash(rm *)"],
  "deny": ["Bash(curl *)"]
}
ListEffect
allowTool call is permitted without asking the user
askTool call requires user approval
denyTool call is blocked outright

Permission Modes

The PermissionMode enum defines 5 evaluation modes that change the default behavior:

ModeDescriptionDefault Decision
DefaultStandard mode — explicit rules apply, unmatched calls require askingAsk
AcceptEditsAutomatically allow file edits (Write, Edit, NotebookEdit)Ask (except edits)
PlanRead-only mode — denies all write/execute tools (Bash, Write, Edit, NotebookEdit)Deny for writes
DontAskNever prompt the user — unmatched calls are allowedAllow
BypassPermissionsAll tool calls are allowed regardless of rulesAllow

Evaluation Order

When a tool call is evaluated, the PermissionEvaluator follows this 6-step process:

Step 1: Mode Short-Circuit

  • BypassPermissionsAllow immediately
  • Plan mode and tool is bash, write, edit, or notebookeditDeny immediately

Step 2: Agent-Specific Overrides

If the agent has its own permissions block in the config, evaluate those rules first:

  • Check deny rules → Deny if matched
  • Check ask rules → Ask if matched
  • Check allow rules → Allow if matched

Step 3: Global Deny Rules

Check project-level deny rules → Deny if matched

Step 4: Global Ask Rules

Check project-level ask rules → Ask if matched

Step 5: Global Allow Rules

Check project-level allow rules → Allow if matched

Step 6: Mode Default

If no rule matched, apply the mode's default decision:

  • DefaultAsk
  • AcceptEditsAllow for edit tools, Ask for others
  • DontAskAllow
  • BypassPermissionsAllow

Permission Decision

The evaluation returns one of three decisions:

DecisionBehavior
AllowTool call proceeds
AskTool call requires user approval (via TUI or hook)
DenyTool call is blocked; error returned to the agent

Configuration

Permissions are configured at two levels:

Project Level

{
  "permissions": {
    "allow": ["Bash(npm run *)", "Bash(cargo test *)"],
    "ask": ["Bash(git push *)"],
    "deny": ["Bash(rm -rf /)"],
    "default_mode": "default"
  }
}

Agent Level

{
  "agents": [
    {
      "name": "reviewer",
      "prompt": "...",
      "permissions": {
        "allow": ["Read(*)"],
        "deny": ["Write(*)", "Edit(*)", "Bash(*)"]
      }
    }
  ]
}

Agent-level rules take precedence over project-level rules (evaluated first in the evaluation order).

  • Configuration — How permissions are configured
  • Tools — The tools that permissions control
  • Hooks — Hook-based permission decisions