Permission modes & approvals
The permission system answers one question: may this tool call run, or must a human be asked first? It has three layers — the mode (what the user selected), project rules (WorkApprovalRules) and tool risk (read / write). Rules always outrank the mode.
The five modes
| Mode | Behaviour | Typical use |
|---|---|---|
plan | Non-read-only tools are refused outright (not "ask then run"), with a fixed refusal message | Let the model design, touch nothing |
readonly | Read-only tools allowed, write tools refused | Reading code, researching, analysis |
ask (default) | Each write operation asks for approval | First pass in an unfamiliar repository |
auto | Routine writes run directly; sensitive operations still ask | Trusted, repetitive work |
bypass | Everything allowed except what rules explicitly deny | One-off tasks in disposable environments |
Invalid values fall back to ask (with legacy read-only / read_only accepted).
Decision order
- Mode hard limits:
plan/readonlyare evaluated first and short-circuit — project rules cannot loosen them. - Project rules: match
WorkApprovalRuleby tool name and path;denyoverrides any mode, andallowalso lets calls through underask/auto. - Mode + tool risk: with no rule matched, read-only tools follow the mode, write tools resolve to "execute" or "request approval".
Rule model
| Field | Details |
|---|---|
ToolName | Tool name, * wildcard supported |
PathPattern | Optional path match; only file tools participate (read/write/delete/move/apply-patch) |
Decision | allow or deny; anything else fails on create |
IsEnabled | Rules can be disabled without deleting them |
Match scoring: an exact tool name beats a wildcard, and a rule with a path beats one without. Duplicate rules for the same tool and path pattern are merged on create to avoid rule churn.
How run modes interact
interactive: keeps the selected mode and confirms step by step.autopilot: promotesasktoauto(fewer interruptions) but never promotesplan/readonly— hard limits are not bypassed by unattended runs.
Differences across the three entry points
| Entry | Permission source |
|---|---|
| Code sessions | All three layers: mode + project rules + tool risk; PermissionMode and AgentMode persist on the session |
| Chat page | Mode + per-tool overrides (ToolApprovalOverrides); project rules are not read — chat has no project context |
| Workflow nodes | Node-level toolApprovals plus the run-level toolApprovalMode, the latter only filling tools without an explicit setting |
Approval round trip
- When the decider returns "needs approval", the loop emits an
approval_requestevent (tool name and arguments). - The UI renders an approval card: approve or reject, optionally with a reason.
- Approval executes that single call and feeds the result back; rejection returns the reason as the tool result so the model can choose another approach.
- Not to be confused with
question:approval_requestasks "may I run this tool",questionasks the user for information. They carry separate ids.
Safety advice
- Start unfamiliar repositories in
ask, then switch toautoonce the tool surface matches expectations. - Write
denyrules for tools that must never run unattended (deleting files, pushing code) — those hold even underauto/bypass. - Use path rules to scope access, e.g. allow writes under
docs/**while the rest still asks. - MCP tools share the same decision path; run a new server in
askonce to see what it actually calls.
Observability and troubleshooting
- The rule API returns
WorkApprovalRuleDto; an invalid decision reportsapprovalRule.invalidDecision, a missing ruleapprovalRule.notFound. - A rejected tool call appears in the message stream as a tool result with the reason — that is why the model "did not do that thing".
- If a call was allowed or blocked unexpectedly, check project rules first (they outrank the mode), then whether the run mode promoted
asktoauto.