Files
oh-my-pi/docs/approval-mode.md
T
can1357 e4a16451ec feat(coding-agent): added coding-agent approval types and mode options
- Added `ToolTier`, `ToolApproval`, and `ToolApprovalDecision` types and exported approval APIs.
- Updated approval-mode options from `auto|prompt|custom` to `always-ask|write|yolo` and defaulted mode to `yolo`.
- Changed approval resolution to apply per-tool decisions first, then mode-tier limits, with legacy-mode migration.
- Assigned read/write/exec `approval` and approval-detail prompts across built-in, custom, extension, and MCP tools.
2026-05-26 21:52:16 +02:00

3.4 KiB

Tool approval mode

Tool approval has two independent inputs:

  1. Tool declaration — every tool may declare an approval tier:
    • read: reads data or updates UI-only session metadata.
    • write: mutates workspace/session state but does not execute arbitrary code.
    • exec: executes code, shells out, drives a browser, spawns agents, or performs similarly broad actions.
  2. User policy — tools.approval.<toolName>: allow | deny | prompt overrides the mode for that tool.

Tools without an approval declaration are treated as exec. This is the safe default for MCP and unknown custom tools.

Modes

Configure with tools.approvalMode:

Mode Auto-approves Prompts for
always-ask read write, exec
write read, write exec
yolo (default) read, write, exec nothing unless a tool declares override: true

--auto-approve and --yolo force tools.approvalMode: yolo for the session. They do not bypass tool safety overrides.

User overrides

tools.approval is honored in every mode:

tools:
  approvalMode: write
  approval:
    bash: prompt
    read: allow
    mcp__filesystem__delete: deny

Resolution per tool call:

  1. Compute the tool's approval decision from tool.approval(args); omitted means exec.
  2. If the decision has override: true:
    • tools.approval.<tool>: deny blocks the call.
    • every other policy prompts, even in yolo.
  3. Otherwise, a valid tools.approval.<tool> value wins.
  4. Otherwise, the active mode auto-approves or prompts by tier.

Invalid policy values are ignored and fall back to the tool tier/mode decision.

Safety overrides

A tool can force a prompt with object-form approval:

approval: { tier: "exec", override: true, reason: "Critical pattern detected" }

bash uses this for critical destructive patterns such as rm -rf /, fork bombs, remote-fetch-then-execute, writes to /etc/passwd, and host shutdown commands. These prompt even in yolo; in non-interactive/headless sessions they fail instead of running unattended.

Per-tool prompt details

Tools can add approval-prompt body lines with formatApprovalDetails(args). The standard prompt includes:

  • Allow tool: <name>
  • Origin: MCP server tool for unannotated mcp__... tools
  • Reason: <reason> when the tool decision supplies one
  • tool-specific details such as command, path, code, browser action, or subagent assignment

Defining approval on tools

Built-in and custom tools share the same shape:

export type ToolTier = "read" | "write" | "exec";
export type ToolApprovalDecision = ToolTier | { tier: ToolTier; reason?: string; override?: boolean };
export type ToolApproval = ToolApprovalDecision | ((args: unknown) => ToolApprovalDecision);

approval?: ToolApproval;
formatApprovalDetails?: (args: unknown) => string | string[] | undefined;

Examples:

approval: "read"

approval: args => LSP_READONLY_ACTIONS.has(args.action) ? "read" : "write"

approval: args => isCritical(args.command)
  ? { tier: "exec", override: true, reason: "Critical pattern detected" }
  : "exec"

Subagents

Subagents run headless with tools.approvalMode: yolo so they do not stall waiting for UI. The parent task approval is the authorization boundary. Tool-level safety overrides still apply; a critical override inside a headless subagent fails rather than running without confirmation.