Tool Permission System (Allow / Ask / Deny)#
Gateway tools in agenta support an explicit three-level permission model: allow, ask, and deny. Permissions operate at two scopes:
- Per-tool: each tool carries a
permissionfield (allow | ask | deny | inherit). - Per-agent default:
runner.permissions.defaultwith four modes:allow,ask,deny, andallow_reads(the default — reads auto-approve, writes trigger human approval).
The model was introduced in PR #4811 and redesigned in PR #5041, replacing the previous fragile session-id-based approach (permissionPolicy, needsApproval) with a single authored-to-enforced contract. The UI surface for canonical gateway tools was standardized in PR #5140.
Key rule: inherit (or unset) falls back to runner.permissions.default; an explicit tool-level value always wins.
Permission Values & Wire Format#
| Scope | Values |
|---|---|
| Per-tool | allow · ask · deny · inherit |
| Agent default | allow · ask · deny · allow_reads (default) |
Semantics :
allow— tool executes without human intervention.ask— runner pauses; apending_interactionevent fires; the frontend renders Approve/Deny buttons.deny— tool is rejected immediately.allow_reads(agent-level only) — read operations execute; write operations escalate toask.
Wire format — the run request carries:
{ "permissions": { "default": "allow_reads", "rules": [...] } }
The legacy permissionPolicy and needsApproval fields are removed (old drafts with these keys are silently ignored) .
UI Components (Agent Template Interface)#
All frontend components live under web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/.
ToolItemControl.tsx — the per-tool permission select rendered in the agent template. Exposes the four values (allow, ask, deny, inherit). Introduced in PR #4811.
ToolFormView.tsx / FunctionToolForm — the drill-in form for an individual tool. In PR #5140, the legacy rendering body was extracted into an inner FunctionToolForm component; canonical gateway tools now route through a CanonicalGatewayToolForm wrapper that fetches catalog details via useToolActionDetail(integration, action), synthesizes the equivalent legacy function shape for display, and delegates to FunctionToolForm. The on-disk draft is never mutated — the synthesized shape is display-only .
toolUtils.ts — exports parseGatewayTool(tool) (normalizes both canonical type:"gateway" objects and legacy slug-encoded function tools into a shared ParsedGatewayTool view) and gatewayToolIdentity(view) (stable identity = provider + integration + action + connection; permission is excluded from identity because it is policy, not identity) .
AgentTemplateControl.tsx — uses selectedGatewayIds (a set of identity strings) and removeGatewayToolByIdentity() to track and remove gateway tools by encoding-independent identity, supporting both canonical and legacy tools uniformly .
Fail-safe: if useToolActionDetail cannot resolve the action, the drill-in renders a warning plus a read-only JSON view instead of crashing .
Enforcement & HITL Flow#
services/runner/src/permission-plan.ts is the single decision module that enforces permissions for both the Pi relay gate and the Claude Code gate . Both gates read from the same parsed permission plan so they cannot disagree.
When a tool is gated with ask:
- The runner pauses execution and emits a
pending_interaction: {id, tool}in the batch response envelope. - The frontend shows Approve/Deny buttons immediately (not held by conversation
busystate) . - Approval decisions are keyed on stable anchors: tool spec name (relay) or recorded
tool_callname (Claude gate) plus canonical args hash . - Each decision is consumed once; a config change to
denyoverrides a stale approval.
For Claude Code gateway tools, the runner generates .claude/settings.json with per-tool MCP rules using the naming convention mcp__agenta-tools__<tool_name>. An allow permission bypasses Claude's own gate; ask preserves HITL .
Playground Support (Roadmap)#
The Agent Template interface has full permission support. The Playground is partially behind:
- The
ToolSelectorPopover,PromptSchemaControl, andToolItemControlcomponents in the Playground still key off the legacy slug encoding and do not yet handle canonicaltype:"gateway"objects . - PR #5140 is explicitly read-path only — both encodings are already equivalent server-side, but the Playground edit path still writes the legacy shape on add and convergence to canonical is deferred .
- The
commitDifflayer keys tools byfunction.name, so canonical tools get positional (not identity-based) change-diff tracking until this is addressed.
The follow-up work is to update these Playground components to read through parseGatewayTool and the identity utilities from toolUtils.ts, mirroring what Agent Template already does.