# Hermes Agent is now the easiest coding agent to govern with ACP

Nous Research's Hermes Agent ships native pre/post tool-call hooks that cover every tool — terminal, file, web, browser, vision, custom skills. No feature flag, no Bash-only gap, no MCP supplement required. Here's what the new acp-hermes plugin does and why Hermes turned out to be the cleanest integration we've built.

[Hermes Agent](https://github.com/NousResearch/hermes-agent) — Nous Research's terminal-native coding agent — now has first-class ACP support via the new [acp-hermes](https://pypi.org/project/acp-hermes/) Python plugin. Three commands to install:

```bash
pip install acp-hermes
hermes plugins enable acp
acp-hermes login   # if this errors about editor/ACP mode, use: python -m acp_hermes.cli login
```

That's it. No feature flag dance. No MCP connector supplement to cover the tools your hook can't see. No "wait, does this fire for `apply_patch` too?" footnote. Every Hermes tool call — terminal, file, web, browser, vision, cron, every custom skill — flows through your ACP workspace from the first restart.

Hermes turned out to be the cleanest integration we've built. This post is about why, and what it tells you about the future of governable coding agents.

## The state of hook coverage in 2026

We've now shipped ACP integrations for a dozen coding-agent harnesses. They cover wildly different fractions of the tool surface:

| Harness     | What `pre_tool_call` actually intercepts                                    | Feature flag? |
|-------------|-----------------------------------------------------------------------------|---------------|
| Claude Code | All native tools + MCP                                                      | No            |
| Codex CLI   | Bash only. Read/Edit/Write/web fetch/MCP do not fire.                       | **Yes** (`[features].codex_hooks = true`) |
| Cursor      | Most native tools. Some built-ins (e.g. Web Search) don't emit. Bugs filed. | No            |
| **Hermes**  | **Everything.** Native and custom. Sync hooks, on by default.               | No            |

This isn't a vendor scoreboard for its own sake. It's a real, structural difference in *what governance means* on each platform.

On Codex, "I've governed my agent" today means "I've governed shell commands my agent runs" — which is most of the destructive-action risk (`rm -rf`, `gh repo delete`, `psql DROP TABLE`), but not all of it. File-edit governance on Codex requires routing edits through an MCP connector ACP also installs. It's a real solution, but it's a *two-layer* solution.

On Hermes, "I've governed my agent" means "I've governed every tool call." Period. No supplementary layer.

## Why Hermes ended up here

Hermes's plugin system was designed for this. Where Claude Code's hooks evolved out of a "shell out and pipe JSON" pattern (and Codex's are essentially a port of that), Hermes hooks are first-class Python callbacks: `register(ctx)` registers a function that Hermes calls in-process before dispatching a tool call. The hook receives the tool name, the arguments, the session ID. Return a `{"action": "block", "message": ...}` and Hermes won't run the call.

That design — in-process Python rather than out-of-process shell — has three consequences worth noting:

1. **Hook coverage is automatic for every tool the agent has.** There's no "but does the hook fire for *this* one?" carve-out, because every tool dispatcher goes through the same code path. Adding a new built-in or custom skill doesn't create a governance gap.

2. **Hooks are synchronous.** The tool call blocks until the hook returns. We bound the worst case with a 4-second HTTP timeout to the ACP gateway, fail open beyond that, and emit a stderr warning when we do — same fail-open posture as the Claude Code plugin. Sync hooks rule out async fire-and-forget patterns, but for a governance hook that's the correct trade-off.

3. **The plugin must be a Python module loadable by Hermes.** We could not ship the same `govern.mjs` we ship to Claude Code / Codex / Cursor. The universal installer now detects Hermes and installs `acp-hermes` for you in connected mode (finding the right interpreter — pipx-injected or Hermes's own shebang — and enabling the plugin); a direct `pip install acp-hermes` still works for local/manual setups.

## How the `acp-hermes` plugin works

Two registered hooks, both calling the same ACP gateway endpoints as the Claude Code / Codex / Cursor plugins:

- **`pre_tool_call`** → POST `/govern/tool-use`. Server returns `allow` / `deny` / `ask`. `deny` and `ask` block the tool call with a system message; `allow` passes through. The plugin sends `X-GS-Client: hermes-plugin/<version>` so per-client policies and dashboard filters work the same way they do for the other harnesses.

- **`post_tool_call`** → POST `/govern/tool-output`. Observational only. Server-side audit, redaction logging, and DLP scanning all run, but the redaction *result* isn't propagated back to the agent's context — that's a Hermes limitation, not an ACP limitation. The `post_tool_call` return value is ignored by design in Hermes today.

The plugin is **zero-dependency** — uses Python's stdlib `urllib` for HTTP. No `requests`, no `httpx`, no `aiohttp`. That matters more than it sounds: every transitive dep on a governance plugin is a credential-adjacent supply-chain target, and the value of a thin wrapper over an HTTP API is being a thin wrapper.

## What you'll see in the dashboard

A new `hermes-plugin` row on [cloud.agenticcontrolplane.com/agents](https://cloud.agenticcontrolplane.com/agents), with activity tagged by Hermes's `task_id` (its session identifier). The activity log shows:

- `tool_name` — `terminal`, `web_search`, `read_file`, plus any custom skill names
- `tool_input` — the dict the model passed
- `tool_output` — the result, truncated to 200 KB
- `duration_ms` — Hermes's measured execution time
- Standard ACP attribution — workspace, user, policy decisions, delegation chain

Same shape as the Claude Code / Codex / Cursor log rows. Same policies apply. Cross-harness teams can mix freely.

## Two real gotchas to know about

**`post_tool_call` is observational only.** If your governance model depends on catching a secret in `terminal` output before it reaches the model, Hermes can't apply the redaction back to the agent's context today. ACP still logs the redaction server-side for post-hoc review and DLP analytics — you just don't get the inline rewrite that Claude Code's `PostToolUse` supports. Most policy violations you actually care about are catchable at `pre_tool_call` anyway.

**No native "ask user" semantic.** Hermes only distinguishes `block` / `not-block`. When the ACP server returns `decision: "ask"`, the plugin renders it as a `block` with the message `"[ACP] Approval required: …; approve in the ACP dashboard and retry."` The user reviews and approves out-of-band, then retries. Adding a `/acp-approve` slash command via Hermes's `register_command` API would give a richer inline UX — open issue, PR welcome.

## What this means for the agent landscape

The harnesses with the cleanest governance stories aren't always the ones with the biggest installed base — they're the ones that designed for it from day one. Hermes's plugin system makes governance plug-in-able by construction. Claude Code's hooks have been extended responsibly since they shipped. Codex's hooks are explicitly marked `Stage::UnderDevelopment` and it shows in coverage.

If you're building a new coding agent and want to be governable: pick a hook surface that fires for every tool dispatch, ship it on by default, make `block` the only operation a hook return value needs to encode, and document the contract. Hermes did all four and it shows.

## Try it

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

Full install guide: [/integrations/hermes](/integrations/hermes).

If you're already running ACP for Claude Code / Codex / Cursor, the same `~/.acp/credentials` works — `acp-hermes login` is idempotent and will short-circuit if you've already authenticated. One workspace, every harness.
