Skip to content

Using AgentsFence with Hermes

This guide maps what you already do in Hermes to what AgentsFence does at each point. For a closer look at particular tasks, see the scenario cookbook.

  • A Hermes session is a task. When you send the first message in a session, AgentsFence compiles that message into a task policy.
  • Your later messages can change it. A message that could change authority (“now email it to bob@…”, “don’t delete anything”) is compiled and merged; the newer instruction wins. Messages that can’t (“thanks, what did the second one say?”) are ignored.
  • Nothing the agent reads can change it. Tool results, web pages, emails and files never reach the compiler.
  • Every tool call is checked before it runs, and gets one of three answers: ALLOW, BLOCK, or ASK_USER (Hermes’ own approval prompt).
  • /new starts a new task with a new policy. --resume continues the same task, with the same policy.
Hermes concept What AgentsFence does What to know
Profiles (hermes -p work) Each profile has its own HERMES_HOME, so its own plugins.enabled, .env, and AgentsFence state ($HERMES_HOME/agentsfence). Enable AgentsFence in each profile you use (hermes -p work plugins enable agentsfence). The agent can’t edit any profile’s config.yaml, .env, auth.json or plugins/. cross_profile writes ask first.
Chat session (hermes chat, TUI) First message → policy. /fence shows it; /fence audit shows recent decisions. ASK_USER uses the normal [o]nce / [s]ession / [a]lways / [d]eny prompt. Every answer applies to that exact action (same command, recipients, paths) in this task only.
One-shot (hermes chat -q "…") Same as a chat session with one message. Hermes still prompts in your terminal for approvals. For unattended runs, phrase the request so it needs no approvals: name the recipients, folders and sites.
The agent loop (turns, tool calls, replanning) Replanning is free: any tool that classifies as a read is fine in a read task. Consequential calls are checked one by one. The agent is told when something is blocked and asked not to work around it. If it genuinely needs more authority, it should ask you in the chat.
/new, --resume, --continue /new drops the policy. Quitting Hermes keeps it, so --resume continues under the same restrictions. Policies expire after 12 hours by default (policy.ttl_hours). After expiry, tool calls are blocked until your next message, which starts a fresh policy (only the task’s owner can restart it in a shared chat).
Gateway (Telegram, Slack, Discord, …) Approvals arrive as the platform’s approval buttons. In group chats, only the person who started the task can widen it; anyone’s “don’t …” still applies. Keep Hermes’ group_sessions_per_user: true (the default). Where a platform doesn’t report who sent a message, AgentsFence can’t tell participants apart. /fence is disabled in the gateway because one process serves many chats; use agentsfence audit on the host.
Toolsets and MCP servers (-t, platform_toolsets, mcp_servers) Hermes decides which tools exist; AgentsFence only narrows. MCP tools (mcp_<server>_<tool>) are classified from their names unless you map them. Map the MCP tools you rely on, especially any whose name doesn’t reveal what it does.
Subagents (delegate_task) Children run under the parent’s policy and share its budgets. A child’s goal (written by the parent model) is never compiled into authority. Delegation can’t multiply authority or call limits.
execute_code Running code is EXECUTION. Tool calls made from inside the code are checked against the same task policy. Once you authorize code execution, what the code does outside Hermes tools is opaque. Use a container backend for such tasks.
terminal Commands are classified by what they do. ls/grep/git status are reads; rm is a delete; curl -d, scp and git push are outbound writes; sudo is a permission change. Write options on “safe” commands (sort -o, rg --pre) are caught. Anything unfamiliar is execution. Approving a command approves that command, not the terminal.
Browser tools Navigating and reading are reads. Clicking, typing and pressing keys are external writes (they can submit forms). browser_console with a JavaScript expression is code execution. Suspicious URLs (long encoded query strings, email addresses in the query) ask, even in a research task.
Memory (memory) Memory writes are local writes and need your authority (“remember that …”). This stops an injected email from planting a persistent instruction.
Skills (skill_manage) Creating or editing a skill is a local write; deleting one is a delete. Skills can change future behaviour; treat skill edits like code edits.
Cron jobs (cronjob tool, hermes cron) Creating a job is EXECUTION, so it needs your authority. When the job runs, its stored prompt is compiled into that run’s policy. Hermes’ approvals.cron_mode defaults to deny, so a cron run can’t get approvals; it only does what its prompt authorizes. Keep that default.
Kanban workers Each worker’s task prompt is compiled like a user message. Authority comes from whoever wrote the task card.
Approval modes See the table below. —
Safe mode (HERMES_SAFE_MODE=1) Hermes skips all plugins, so AgentsFence is off. agentsfence doctor warns when safe mode is on.
Other plugins If other plugins also decide tool calls, AgentsFence puts itself first when it loads and at every turn, so their approve can’t hide its block, even on the first tool call. agentsfence doctor lists them. Set plugin_conflicts: strict if you also want AgentsFence’s questions never to hide their blocks.
Hermes setting ASK_USER becomes BLOCK
Default (manual prompts) a prompt you answer blocked
--yolo, approvals.mode: off auto-approved. With strict_yolo: true, a block instead. Under the read-only fallback policy, always a block. blocked
Session / permanent allowlist hit (you answered [s] or [a] before) auto-approved for that exact action; blocked under fallback or with strict_yolo: true blocked
No human present (script without a terminal, gateway with no listener) denied blocked
Cron, cron_mode: deny (default) denied blocked
Cron, cron_mode: approve auto-approved; blocked under fallback or with strict_yolo: true blocked

A BLOCK is never affected by an approval mode.

Instead of Say Why
“Email John the notes.” “Email the notes to john@example.com.” Names aren’t addresses; without one, every send asks.
“Summarize my inbox.” “Summarize my inbox. Don’t send or delete anything.” Without the ban, sends are questions; with it, they are blocked.
“Clean up my downloads.” “Delete the duplicate files in ~/Downloads/dupes.” Deletion needs your explicit words, and the path bounds it.
“Research X.” “Research X using only en.wikipedia.org and docs.python.org.” Domains you name after “only use” or “using only” become an allowlist.
“Book a flight.” “Find flights to Boston; don’t pay for anything.” Purchases are blocked by default anyway; saying so makes the reason explicit.
  • /fence in the CLI shows the active policy, including approvals you’ve given in this task.
  • agentsfence audit -n 30 shows recent decisions. Message bodies and free text are redacted to a length and a hash.
  • agentsfence doctor checks that the plugin is enabled in this profile, that the compiler is configured, and whether safe mode or other directive plugins are present.