Skip to content
Agentic Control Plane

Govern Nous Research Hermes Agent with Agentic Control Plane

Hermes Agent ships a first-class Python plugin system with synchronous pre_tool_call and post_tool_call hooks that cover every tool the agent runs — terminal, file, web, browser, vision, cron, custom skills. Unlike Claude Code or Codex CLI (where hook coverage is partial), Hermes hooks are universal and on by default. That makes it the cleanest of the major coding agents to govern.

TL;DR

pip install hermes-acp
hermes plugins enable acp
hermes-acp login

Three commands. No curl-pipe-bash, no feature flags to flip, no MCP connector required for tool coverage. The first line installs the hermes-acp PyPI package. The second registers it with Hermes. The third opens your browser, exchanges a one-time auth token for a workspace API key, and writes it to ~/.acp/credentials.

You’ll see your first governed tool call in cloud.agenticcontrolplane.com/logs within seconds of restarting hermes.

Why pip instead of curl-pipe-bash?

The Claude Code / Codex / Cursor installer is curl -sf https://agenticcontrolplane.com/install.sh | bash because those harnesses use out-of-process shell hooks — bash spawns govern.mjs, pipes JSON in on stdin, reads JSON out on stdout. The installer just drops one file in the right place.

Hermes hooks are in-process Python callbacks: register(ctx) registers functions that Hermes calls directly inside its own runtime. There’s no subprocess boundary, no stdin/stdout protocol — it’s a function call. That means the integration must be a Python module loadable by Hermes’s plugin system, which means pip. Forcing pip inside a curl bash wrapper would be a worse UX — bash shelling out to whichever Python it finds first, two uninstall paths, and confusing failure modes when one half fails.

So: separate install paths, native to each runtime. Same backend, same dashboard, same policies.

How it works

ACP registers two Hermes hooks via the hermes_agent.plugins.acp entry point:

Hook What it does
pre_tool_call POSTs to /govern/tool-use. Server returns allow / deny / ask. deny blocks with a system message; ask escalates to Hermes’s native approval prompt; allow passes through.
post_tool_call POSTs to /govern/tool-output for observation. Server-side audit, redaction logging, and DLP scanning all apply.

Both hooks send X-GS-Client: hermes-plugin/<version> so the dashboard, policy router, and audit log can distinguish Hermes traffic from Claude Code / Codex / Cursor / etc.

The plugin is zero-dependency — it uses Python’s stdlib urllib for HTTP, no requests / httpx / aiohttp pulled in. This keeps the supply-chain surface tight and matches the discipline we use for the rest of the ACP packages.

What gets installed and where

Path Purpose
<venv>/site-packages/acp_hermes/ Plugin code — __init__.py, cli.py, plugin.yaml
<venv>/bin/hermes-acp CLI entry point (login, status, logout)
~/.acp/credentials Bearer token from browser OAuth (chmod 600)
~/.hermes/config.yaml (Hermes-owned) plugins.enabled.acp: true after hermes plugins enable acp

No system-level file writes. Uninstall is hermes plugins disable acp && pip uninstall hermes-acp.

Coverage — Hermes is the cleanest of the major coding agents

Harness What pre_tool_call covers
Hermes Everything. terminal, file, web, browser, vision, cron, skills, MCP
Claude Code All native tools + MCP
Codex CLI Bash only (rest needs MCP connector supplement)
Cursor Most native tools; some built-ins (e.g. Web Search) don’t emit

This is why Hermes doesn’t need an MCP connector supplement the way Codex does — the hook surface already covers the full tool catalog.

Limitations — read before relying on Hermes hooks alone

post_tool_call is observational only

Hermes’s post_tool_call hook return value is ignored — by design. The hook can log and forward output to the gateway, but it cannot block or redact a tool’s result the way Claude Code’s PostToolUse can. If your governance model depends on post-hoc redaction (catching a secret in terminal output before it reaches the model), this is a gap.

ACP works around this two ways:

  • Pre-call denial covers the destructive path. Most policy violations you actually care about — rm -rf, exfiltration, credential reads — are catchable at pre_tool_call, which Hermes fully supports.
  • Server-side audit still fires. Output is still POSTed to /govern/tool-output. The redaction is visible in the dashboard for post-hoc review and DLP analytics, just not applied back to the agent’s context.

Native inline approvals (hermes-acp ≥ 0.1.1)

When the server returns decision: "ask", the plugin escalates to Hermes’s own human-approval gate — the same inline [o]nce / [s]ession / [a]lways / [d]eny prompt Hermes uses for dangerous shell commands. You answer in the terminal, mid-run, without leaving the agent. An [a]lways answer is scoped to the specific tool ACP flagged (the plugin passes rule_key: acp:<tool>), so a standing approval never silently widens.

Versions ≤ 0.1.0 hard-blocked ask decisions with a “approve in the dashboard and retry” detour — pip install -U hermes-acp to get the native flow.

Hooks are synchronous

Hermes calls hooks synchronously, blocking the tool call until the hook returns. We use a 4-second HTTP timeout to /govern/tool-use to bound the worst case. Beyond the timeout, the plugin fails open — the tool call proceeds and a [ACP] gateway unreachable warning is written to stderr. Server-side per-tenant failMode: closed is on the roadmap if you need the inverse.

What you’ll see in the dashboard

Once Hermes is governed, cloud.agenticcontrolplane.com/agents shows a hermes-plugin row with activity. Tool calls are tagged with the Hermes task_id (Hermes’s session identifier — the gateway maps this to ACP session context).

The activity log includes:

  • tool_name — e.g. terminal, web_search, read_file, plus any custom skill tool names
  • tool_input — the dict the model passed to the tool
  • tool_output — the result, truncated to 200 KB (matches the backend scan ceiling)
  • duration_ms — Hermes’s measured execution time
  • Standard ACP attribution — workspace, user, policy decisions, delegation chain

Note what’s not in that list: model cost. The plugin hooks tool calls, so it never sees the model’s token usage — you get governance and a full tool audit, but not spend. For an autonomous, API-metered agent like Hermes, cost is exactly what you want to watch. Turn it on with one more change.

Get the cost X-ray — route Hermes’s model calls through the proxy

The plugin above is one of ACP’s two capture points. It governs and audits every tool call. The other capture point — the model proxy — meters every model call, and that’s where cost, prompt-cache hit rate, and the loop-vs-leaf cost X-ray come from.

For Hermes especially, this is the high-value half: it runs unattended, often against a paid API, so a cost regression surfaces on next month’s bill instead of the moment it happens. The proxy makes spend visible per call, per run, and per agent in real time.

To turn it on, point Hermes’s model endpoint at the ACP proxy and authenticate with your ACP key (gsk_...) instead of the raw provider key. Wherever Hermes configures its model backend — an OpenAI-compatible base URL, or the Anthropic ANTHROPIC_BASE_URL — set it to:

# OpenAI-compatible model backends
#   base_url = https://api.agenticcontrolplane.com/v1
# Anthropic-native model backends
export ANTHROPIC_BASE_URL="https://api.agenticcontrolplane.com/anthropic/v1"
export ANTHROPIC_AUTH_TOKEN="gsk_yourslug_..."   # an ACP key, not a provider key

The proxy is multi-provider (routes gpt-*, claude-*, gemini-* by model id) and forwards to the real provider unchanged — same responses, now metered. It also governs the tool-use blocks the model emits, so the proxy adds a second layer of tool governance on top of the plugin’s hooks.

Full walkthrough, including the per-agent tagging header and what each metric means: Turn on Cost X-Ray.

Run both together for complete coverage: the plugin governs tools at the point they execute (including native terminal/file/browser actions that never hit the model API), and the proxy meters cost and governs the tools the model asks for.

Setting up policy

The same three-axis model applies (Tool / Agent / User policies). Specifically for Hermes:

  • Tool policies — restrict terminal subcommands the same way you would for Claude Code’s Bash. Pattern-based denylist + scoped allowlist works identically.
  • Agent policies — Hermes can run as a CLI, a messaging gateway (Discord/Telegram/Slack/etc.), or an IDE integration. Each surface sends a distinct client_id in the gateway request and can carry a different policy.
  • User policies — gate Hermes enterprise installs to specific identities. Multi-platform attribution lets you write “Slack-platform Hermes can read but not write.”

CLI reference

hermes-acp login       # browser-based authentication + workspace provisioning
hermes-acp status      # check creds + gateway reachability
hermes-acp logout      # remove ~/.acp/credentials

hermes-acp login is idempotent — running it on a machine that already has credentials prints Credentials already at ~/.acp/credentials. Re-run with --force to reconfigure.

Troubleshooting

Hook isn’t firing at all. Confirm the plugin is enabled: hermes plugins list. If acp isn’t in the enabled list, run hermes plugins enable acp. After enabling, restart hermes.

No audit events appearing. The plugin reads ACP_BEARER_TOKEN from the environment of the process that launched hermes. If you exported it after starting Hermes, restart. Alternative: confirm ~/.acp/credentials exists and is non-empty (hermes-acp status).

[ACP] gateway unreachable in stderr. Network failure — plugin failed open. Tool calls are proceeding without governance. Check curl https://api.agenticcontrolplane.com/govern/health.

Every tool call prompting for approval. Server is returning decision: "ask" for everything. Check your workspace’s policies — likely an over-broad “ask on any high-risk tool” rule. Answer [s]ession to quiet the current run, then tighten the rule (or keep [a]lways-ing the tools you trust — each answer is tool-scoped).

Tool call blocked but no entry in dashboard. Pre-call check returned a network error AND a stale denial cache held over from a prior session. Run hermes-acp logout && hermes-acp login to refresh.

  • Claude Code — different runtime (shell-hook), same backend contract
  • Codex CLI — partial hook coverage, needs MCP supplement
  • Cursor — IDE-based, hook-based governance
  • Agent-to-Agent governance — how delegation chains carry identity through Hermes’s session lineage