Skip to content

How it works

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

Plans are written by the model, and the model is the party that prompt injection manipulates. So AgentsFence never takes authority from the plan. Authority comes from you, once, when the task starts.

your message
│ strip quoted / pasted / forwarded text
▼
compiler (OpenRouter · Hermes model · offline rules)
│ JSON draft, schema-validated ── on failure ──► read-only fallback
▼ │
hardening (verbatim evidence · literal targets · prohibitions) │
▼ ▼
TaskPolicy ◄─────────────────────────────────────────────────────────┘
  1. Only your message is used. Tool output, conversation history and subagent goals are never passed to the compiler.
  2. Quoted material is removed. Fenced code blocks, > quotes, forwarded or replied email tails, pasted-content tags and long quoted strings are data you’re showing the agent. They grant nothing.
  3. Structured output. The compiler returns JSON that is validated against a schema. Invalid output is retried once, then the fallback is used.
  4. Hardening only removes authority:
    • every consequential permission needs a verbatim quote from your instruction that contains a request for that kind of action: a clause that opens with the action’s verb. “Summarize my notes” can’t justify a delete, “don’t email bob” can’t justify an email, and “list save and delete commands” remains informational. A summary may also request a concrete output path or email address; for other actions, use a separate sentence;
    • evidence is checked per risk class: a request to send a message doesn’t authorize a CRM update, even though both are “external writes”;
    • recipients must be the object of a send or share request in your words (“the email from alice@…” names a sender, not a recipient); a domain named for browsing cannot authorize other send recipients. Domains and paths must appear as whole tokens, and roots such as / or your home directory are refused;
    • “don’t send / only read / don’t buy / never delete” become prohibitions that override the compiler. They are read from your whole message, even if a long message is truncated for the compiler;
    • wildcard grants, and grants for tools AgentsFence can’t classify, are dropped.

The result is stored per Hermes session, and a short note tells the model a policy is in force.

Later messages in the same session are compiled only if they could change authority (“now email it to john@example.com”, “don’t send anything”). Newer explicit statements win, and “only … / … instead” replaces earlier recipients rather than adding to them. In a shared chat, only the person who started the task can widen it.

For each tool call, before it executes:

  1. Classify the call by what it can do: a capability (email.send) and a risk class (COMMUNICATION). Arguments count: browser_console with a JavaScript expression is code execution, and a multi-file patch is checked against every file it names. Recipients, URLs and paths are extracted from the arguments.
  2. Evaluate it deterministically against the policy. See Evaluation order.
  3. Answer:
    • ALLOW: Hermes runs the tool.
    • BLOCK: the tool never runs. The model receives the reason and is told not to work around it.
    • ASK_USER: Hermes’ approval prompt asks you. See Approvals & grants.

No LLM is involved in stage 2. It is plain code over the policy and the arguments, so it can’t be talked out of a decision. A BLOCK always wins over a question.

A policy describes an envelope (which kinds of effects, toward whom, how many times), not a list of steps. A READ-only policy allows every read tool, including ones nobody anticipated. The agent can switch from Gmail to Drive to the web without asking. It can’t send, delete, share or pay unless you said so.

Situation Result
AgentsFence raises an internal error in pre_tool_call BLOCK (Hermes would otherwise run the tool)
AgentsFence can’t start (bad config) a stub hook blocks every tool call
Another plugin returns approve for a call AgentsFence blocks AgentsFence moves its hook first when it loads and at every turn, so its BLOCK is what Hermes acts on, from the first tool call on
Compiler unavailable or returns garbage read-only fallback, prohibitions preserved; its questions become blocks if Hermes would auto-approve
No policy exists for the session read-only fallback (or BLOCK with missing_policy: block)
A call carries only a task id nobody knows BLOCK
ASK_USER with no human present Hermes’ approval gate blocks
Policy expired (default 12h) BLOCK until your next message starts a fresh policy