Inside its sandbox a coding agent holds ambient authority: naming a file or a command is enough to act on it. So an instruction hidden in a README, a project instruction file, or the output of a command it just ran can make it do things nobody asked for. CapScope stops trying to make the model recognise malicious text and instead takes the authority out of the context entirely — a ceiling of allowed effects frozen before any untrusted byte is read, sliced per sub-agent, stored on the host. Across 300 runs the injected action executed 47 of 75 times under ordinary privileges and 3 of 75 under capabilities — while the model was still argued into proposing it in 34 of those runs.
A coding agent reads files, runs commands and writes patches with the privileges of whoever launched it. There is no distinction inside that sandbox between "the user asked for this" and "some text I read asked for this" — both arrive as tokens in the same context, and both name resources the agent is already permitted to touch. That is ambient authority, and it is the default in every agent harness that ships with a tool list and a system prompt.
The study plants an injection in five surfaces, one at a time, and counts how often the injected effect actually happens. Step through them and compare a tight per-task policy applied to every principal against per-agent capabilities.
Note which surface survives. The project instruction file is the only one where anything gets through under capabilities — three runs of fifteen — and it is exactly the file the harness is designed to treat as guidance. Every other surface goes to zero, because the model can be persuaded all it likes and still has nothing to persuade the host with.
Strip away the name and the idea is small. A capability is written Cap[e, φ]: an effect type e — read, write, or execute — and a predicate φ that has to accept the arguments. For files, φ is a canonicalised path prefix. For commands, it is an allowed prefix of the argument vector. Nothing else.
The part that matters is where they live. Capabilities sit in a store on the host, keyed by which agent issued the call. The model never sees them and cannot name them; it only ever discovers what it holds by proposing a call and being refused. So text arriving later — from a file, from stdout, from a sub-agent's reply — has no vocabulary in which to grant itself anything.
Compose a call against a principal's store and watch the check run.
Two calls in that list are the injection's. Under ambient authority they are indistinguishable from the repair's own calls, because the only thing separating them is intent, and intent is not a property the filesystem can check. Under capabilities they are separated by scope, which is.
The ceiling comes from a preflight pass over the two things that are trusted: the user's request, and the project's file tree. That pass predicts the permissions the task will need, the host validates and freezes the result, and only then does the agent start reading repository contents. Anything an injection says arrives after the ceiling is already fixed.
Delegation then narrows rather than copies. When the orchestrator hands a subtask to a sub-agent, the sub-agent's store may only contain capabilities structurally inside the delegating ceiling. Two sub-agents therefore hold different slices, and a capability granted to one is not available to the other — which is the property a single global policy, however tight, cannot express.
This is the whole argument against policy-as-configuration. A global policy has to be the union of everything any principal in the workflow legitimately needs, so the moment you add a sub-agent that needs to write, every sub-agent can write. Derivation makes the union a ceiling instead of a grant.
Four conditions, seventy-five runs each. Ordinary sandbox privileges. One static policy that blocks sensitive paths and obviously destructive commands. One policy tightened per task from the trusted description, applied to every principal. And per-agent capabilities.
The tightened global policy is the honest comparison and it is the one that shows the limit of the approach: it cuts the attack from 47 runs to 33 and costs four repairs, because a policy narrow enough to stop a sub-agent overreaching is also narrow enough to stop the orchestrator doing its job. Capabilities land at three attacks for the same four repairs, and roughly twice the wall clock.
This is the sentence to take away. In 34 of 75 runs the model was still argued into proposing the injected action, and in 3 it happened. The defence does not work by making the model wiser, and it does not degrade when the model is fooled — which is exactly the property a prompt-based defence lacks, since a prompt-based defence is the model's judgement.
Take a multi-agent workflow of your own — an orchestrator that farms out reads, edits and test runs. Set how many sub-agents it delegates to, pick how you authorise them, and see what a single injection lands in the hands of. The per-run execution rates are the paper's; the workflow shape is yours.
Under ambient authority and under a single policy, the curve is flat: adding sub-agents adds attack surface and changes nothing about what any one of them can reach, because they all hold the same thing. Only derivation makes the extra sub-agent cheap.