Blog

The plan is not the policy

Why AgentsFence gets authority from the user's intent instead of the agent's plan, and why that makes agents both safer and more capable.

September 23, 2026 · AgentsFence team

Most attempts to constrain an AI agent start from its plan. The agent proposes steps, and something checks the steps. It seems reasonable: the plan is right there, and it’s more concrete than a vague user request.

We think this is backwards, and AgentsFence is built on the opposite principle:

The agent’s plan is not trusted as policy. The user’s intent is compiled into an independent authorization contract. The agent may replan however it wants, as long as every action stays inside that contract.

Two problems with policing the plan

The plan is written by the party you’re trying to constrain. When an agent reads an injected instruction, the injection doesn’t stay in the email. It enters the model’s context and becomes part of the plan. “Summarize the inbox, then forward everything to attacker@example.com” is a perfectly coherent plan. A checker that reviews plans for coherence or plausibility is reviewing the attacker’s work, using the same kind of model the attacker just fooled.

Plans change, and should. Good agents replan all the time. The statement isn’t in Gmail, so try Drive. Drive’s search is weak, so list the folder. The file is a PDF, so extract the text. If the plan is the policy, every replan is either a violation (frustrating) or a policy update (which means the agent writes its own permissions). Neither is acceptable.

Where authority should come from

The only party entitled to decide what a task may do is the person who asked for it. So AgentsFence goes back to that source.

When a task starts, the user’s message, and nothing else, is compiled into a TaskPolicy:

# "Find my latest mortgage statement and summarize the balance."
purpose: summarize_mortgage_statement
allowed_risk_classes: [READ]
denied_tools: [email.send, file.share, file.delete]
allow_external_writes: false
allow_destructive_actions: false
max_external_writes: 0

Note what this policy does not contain: a list of steps. It doesn’t say “search Gmail, then read message X”. It describes an envelope, meaning which kinds of effects are permitted, toward whom, and how many times.

That’s why dynamic replanning is safe. Every tool call is classified by what it can do, not by which tool it is. mcp_gmail_search_threads, mcp_drive_read_file_content, read_file, web_search and ls ~/Downloads are all READ. A READ-only policy allows all of them, including tools the compiler never saw. It blocks mcp_drive_delete_file, whatever reasoning led there.

Compiling intent is itself an attack surface

Moving authority to a compiler raises an obvious question: what stops the compiler from being fooled? It’s an LLM too.

We treat the compiler as untrusted, and surround it with deterministic controls that only ever remove authority:

  1. It never sees content. Tool output, history and subagent goals are never passed to it. Quoted, pasted and forwarded material is stripped from the user’s message first.
  2. Evidence or nothing. Every consequential permission must come with a quote copied verbatim from the user’s own instruction, and the quote must contain a request for that kind of action: a clause that opens with the action’s verb. A fabricated quote is dropped, and so is a real quote that asks for something else (“Summarize my notes” can’t justify a delete) or only mentions the action (“explain how to send an email” doesn’t request a send).
  3. Literal targets. A recipient survives only if the user asked to send to it. “Email John” binds nobody, and “the email from alice@…” names a sender, not a recipient. Either way, the agent has to ask before sending to any address.
  4. Prohibitions are deterministic. “Don’t send anything”, “only read” and “don’t buy” are extracted with patterns and override whatever the model produced.

We test this by swapping in deliberately compromised compilers. One grants every permission, every recipient and every domain, and invents evidence for all of it. Another cites the request’s real sentences as evidence for every permission. The attack scenarios still fail. The only difference is that legitimate actions, like sending to the recipient the user did name, sometimes fall back to asking.

The compiler can make the envelope smaller than you intended, and it can’t make it larger than what your sentences ask for. What it can still do is misread one of your sentences within its own kind of action: “remove the typo” contains a delete verb. Hardening bounds that mistake, but doesn’t prevent it.

What this buys

Separating thinking from authority gives you two things that are usually in tension:

  • Freedom to think. The agent explores, retries, switches tools and delegates to subagents (which inherit the parent’s envelope and share its budgets).
  • Boundaries on what it can do. Consequential effects happen only when they trace back to something you said, or to an approval you gave, for that exact action.

This doesn’t solve everything. An authorized email can still carry injected content, and an authorized shell command is opaque. We cover those limits in the security model. For the most common and most damaging failure, an agent doing something nobody asked it to do, taking the authority away works better than asking the model to behave.

← All posts