Agent Permission Models: Who Decides What a Tool May Do
Per-call prompts, session grants, capability scoping and policy engines. How the main agent permission models behave once real work runs through them.
Every agent that touches a real system needs an answer to one question: before a tool call runs, who decided it was allowed? The answer is a permission model, and most teams arrive at one by accident rather than choosing it.
The accidental model is usually a per-call confirmation prompt, because it is the obvious first implementation. It also degrades faster than any alternative, for reasons worth understanding before you build on it.
Per-call confirmation and why it erodes
Ask the user before every mutating call and you have a model that is trivially correct and completely dependent on the user reading the prompt.
They will, for about twenty calls. Then the approval becomes reflex. A developer supervising an agent through a refactor sees dozens of near-identical file writes, and by the tenth the decision has collapsed into a keystroke. The gate is still present in the code and absent in practice.
The failure is structural, not a matter of discipline. Any control whose cost is paid on every action and whose benefit is realised rarely will be optimised away by the person paying. Designing around it means asking for approval only where the decision is genuinely non-obvious.
Session grants
The natural correction is to widen the unit of consent: approve a class of action once, for this session, rather than each instance. Allow writes under this directory, allow running the test suite, allow reading anything.
This matches how people actually think about delegation and it eliminates most of the fatigue. The cost is that consent is now granted before the specific action is known, which means the grant boundary has to be one you would accept for any action inside it.
That works when the boundary is spatial — a directory, a repository, a database schema — and works poorly when it is semantic. Allow safe refactors is not a boundary, because nothing in the system can evaluate it. Grants must be expressible as a check the code can run without asking the model what it thinks.
Capability scoping
The strongest model removes the question rather than answering it. Instead of a general tool plus a rule about when it may be used, expose a narrower tool that cannot do the disallowed thing.
A tool that writes only under the repository root does not need a permission check for writes outside it. A database tool that takes a query and refuses anything but a read does not need a rule against writes. The constraint moves from policy into the interface, where it cannot be argued around by a cleverly phrased argument. Tool schema design covers writing tools this way.
Capability scoping is more work up front and much less work forever after, because scoped tools do not need monitoring, do not fatigue anyone, and do not have to anticipate phrasings. Where you can express a rule as a narrower tool, do that instead of writing the rule.
Policy engines
At larger scale the rules stop fitting in tool definitions and want to live somewhere central: a policy evaluated on every call, given the tool name, the arguments, the identity of the requester and the context.
This buys uniformity across many agents and an auditable history of what the rules were on a given day. It costs a new component in the hot path of every tool call and a policy language your team has to be fluent in.
It is the right answer when multiple teams run agents against shared infrastructure and the wrong answer for a single agent, where it adds a distributed system to a problem a scoped tool would have solved.
Identity is the part people skip
Permissions attach to an identity, and the agent needs one of its own rather than borrowing a developer's.
Running an agent under a human account means every action is attributed to that human, permissions are as wide as their access, and revoking the agent means revoking the person. It also destroys the audit trail exactly when you need it, because nothing distinguishes what the agent did from what the developer did.
Give agents service identities scoped to their task, with credentials that can be rotated and revoked independently. This is standard practice for services and inexplicably rare for agents, which have broader capabilities than most services do. Agent audit logging depends on it.
Failing closed and saying why
An unmatched request should be denied. The interesting design question is what the denial message says.
A bare refusal invites the model to retry the same call, or to look for a route around it — neither of which is what you want. State what was denied and why, and where possible name the action that would be permitted instead.
That turns the boundary into information the model can plan around, which converts a loop into a single redirected step. It also makes the denial log readable, since each entry explains itself. Detecting agent loops covers the repetition this prevents.
Choosing between them
Prefer capability scoping wherever the rule can be expressed structurally, because it is the only model with no ongoing cost. Use session grants for the spatial boundaries left over, and set them at the start of the task rather than mid-flight.
Reserve per-call approval for the small set of actions that are irreversible or externally visible: deploying, pushing to a shared branch, sending anything to a person, spending money. Those are rare enough that the prompt still gets read. Approval gates in agents covers placing them, and human in the loop design covers the review experience around them.
Common questions
Why does per-call approval stop working?
Its cost is paid on every action and its benefit is realised rarely, so the person paying optimises it away. After a few dozen near-identical prompts the approval is a keystroke, and the gate exists in the code but not in practice.
What makes a good session grant boundary?
One the code can evaluate without asking the model. Spatial boundaries work — a directory, a repository, a schema. Semantic ones like "allow safe refactors" do not, because nothing in the system can decide what counts as safe.
Should an agent run under a developer's account?
No. It inherits that person's full access, cannot be revoked independently, and destroys the audit trail because nothing distinguishes agent actions from human ones. Give the agent a service identity scoped to its task.