Hermes agent hooks let you run your own code at fixed points in the agent's life: before a tool runs, before the model is called, when a session starts, when a subagent finishes, and more. Hermes Agent is built by Nous Research, and its docs list four hook systems: plugin hooks, shell hooks, gateway event hooks and outbound webhooks. The newest change landed on 1 November 2026: a pre_llm_call hook can now block a whole turn before any model request is made.
📺 Watch: NEW Hermes Agent Update Changes AI!
🔥 Get the Agent OS as a free bonus: AI Profit Boardroom members get the full Agent OS zip, prompt libraries, daily tutorials and weekly live coaching calls. → Get inside
If you handle client data, that matters: a policy hook can stop a prompt cold, and Hermes stores only a placeholder in its place.
What Hermes agent hooks are
A hook is a callback: Hermes fires an event and your code runs. The Event Hooks page in the docs splits them into four systems:
- Plugin hooks: Python callbacks registered with ctx.register_hook inside a plugin's register function. They run in the CLI and the gateway. The docs list tool interception, metrics and guardrails as the main uses.
- Shell hooks: a hooks block in your profile's config.yaml that points at any script with a shebang. Bash, Python, a Go binary, anything. They run in CLI, gateway, Desktop, TUI and dashboard chat sessions.
- Gateway event hooks: a folder in ~/.hermes/hooks/ holding a HOOK.yaml and a handler.py. They fire only in the messaging gateway (Telegram, Discord, Slack, WhatsApp, Teams) on events like gateway startup, session start and commands.
- Outbound webhooks: a hooks.outbound list that pushes signed lifecycle events to external HTTP endpoints such as CI or dashboards.
If you want to build agent systems like this without starting from zero, the AI Profit Boardroom ($69/mo) is where I share the setups, prompts and workflows I use every week.
Hermes agent hooks by type: observers vs control
The plugins page groups the shipped plugin hooks into three categories. The category decides what your hook is allowed to do.
| Category | Hooks (examples) | What the return value does |
|---|---|---|
| Directive/control | pre_tool_call, pre_llm_call, pre_verify, pre_gateway_dispatch | Can block, approve, rewrite or inject. Hermes reads specific return shapes. |
| Transform | transform_tool_result, transform_terminal_output, transform_llm_output, pre_transcription | The first valid string replaces the content. |
| Observer | post_tool_call, post_llm_call, on_session_start, on_session_end, subagent_start, subagent_stop, on_stream_delta, post_compaction, pre_command | Ignored. They watch and log only. |
The full list also covers API request, approval and kanban task events. The docs treat the Event Hooks catalog as the canonical list.
pre_tool_call: block, approve or rewrite a tool
pre_tool_call fires right before every tool runs. If the model calls three tools at once, it fires three times. Your callback can return an action of block with a message, and the tool never runs. The message goes back to the model as the error. It can return an action of approve, which sends the call to the human approval gate. Or it can return an action of modify with new args, which are merged over the originals before the tool runs.
Block always beats approve, whatever order the plugins registered in.
pre_llm_call: inject context into the turn
pre_llm_call fires once per user turn, after context compression and before the main loop. Return a context string and Hermes appends it to the user message for that turn. It never touches the system prompt. The docs say this is on purpose: the system prompt stays the same across turns, so the prompt cache keeps working.
The docs list memory recall, RAG context, guardrails and per-turn analytics as the main uses.
📺 Watch: NEW Hermes Agent Updates are INSANELY GOOD! 🤯
New in Hermes agent hooks: pre_llm_call can block a turn
Commit ad9229c was authored on 31 October 2026 and merged to main on 1 November 2026 (UTC). The latest tagged release is v0.22.5 from 23 October 2026, so this is on main and not in a tagged release yet.
Here is what it does, per the docs and the commit:
- A pre_llm_call callback, from a Python plugin or a shell hook, can return an action of block with a message.
- The turn makes zero model calls. The message is shown and saved as the assistant reply, so resume still works.
- The result carries the exit reason blocked_by_plugin_pre_llm_call.
- The first block wins. A block with no message is ignored.
The commit message calls this the supported seam for DLP, privacy and policy plugins that previously had to patch core.
The placeholder: where the blocked prompt goes
Follow-up commit 792330d closed a gap where the blocked prompt could still reach a model later through compaction or search.
Now the blocked prompt is stored only as a placeholder that says the message was withheld by a pre_llm_call policy hook. That placeholder is what later turns replay. It is also what compaction summaries, session_search, external memory providers and the background memory review read. The text you typed is kept only as display metadata, so the CLI resume recap, the TUI, the desktop app and the dashboard still show it to you.
Two more fixes:
- c0bcfe2: a blocked turn runs no post-turn compaction, so it makes no side model call either.
- 4061818: /retry and /undo after a block use the text you typed, not the placeholder. /retry sends it again, so the hook checks it again. More on those commands in the Hermes slash commands guide.
A blocked turn also skips session titling and external memory prefetch and sync.
📺 Watch: NEW Hermes Plugin Update is INSANELY GOOD! 🤯
Hermes agent hooks timeouts: fail open vs fail closed
This is the setting that decides whether a broken hook lets things through or stops them.
For Python plugin hooks, plugins.hook_callback_timeout is 30 seconds by default. Set it to 0 to turn it off. The max is 600. When a bounded callback runs past it, Hermes abandons it so the agent loop keeps moving.
- pre_tool_call fails closed. A callback that times out or raises blocks the tool. The docs say a hung callback is then skipped for a 60-second window before it runs again.
- pre_llm_call fails open. A callback that raises or times out is logged and skipped, and the turn goes ahead. The docs tell policy hook authors to catch their own errors and return a block when they can't decide.
- pre_gateway_dispatch fails open. A broken callback lets the message through, so one bad plugin can't block a whole profile's traffic.
Shell hooks have their own per-entry timeout: 60 seconds by default, capped at 300. They fail open by default too. Add fail_closed set to true on a pre_tool_call entry and a missing script, a timeout, a bad exit or junk output all block the tool instead.
Setting up Hermes agent hooks as shell hooks
- Write a script. By convention it lives in ~/.hermes/agent-hooks/.
- Add it under the hooks block in config.yaml, keyed by event name, with a command, an optional matcher regex (pre_tool_call and post_tool_call only), an optional timeout and an optional fail_closed.
- Run Hermes. The first time each event and command pair fires, Hermes asks for consent. The hooks_auto_accept setting skips that prompt.
- Check what's configured with hermes hooks list.
Hermes sends the script a JSON payload on stdin and reads JSON back from stdout. A pre_tool_call script that exits with code 2 blocks the tool, which matches the Claude Code and Cursor convention. A pre_llm_call script can return either an action of block with a message or a decision of block with a reason.
One warning from the docs: gateway hooks in ~/.hermes/hooks/ load with no sandbox. Read any handler.py before you drop it in.
Practical uses for Hermes agent hooks
Where hooks earn their keep:
- Guardrails. A pre_tool_call hook with a matcher on terminal, write_file and patch can block risky commands or scan for secrets, with fail_closed on. A pre_llm_call hook can now stop a prompt that contains a customer ID before it ever leaves your machine.
- Memory recall. A pre_llm_call hook can pull notes from a memory store and add them to each turn. See the Hermes memory system guide and the best Hermes Agent memory options.
- Logging and audit. post_tool_call, post_llm_call and subagent_stop are built for audit logs, metrics and alerts. With heavy delegation, subagent_stop fires many times per turn, so keep it fast. More on that in the Hermes subagents guide.
- Stream watchers. on_stream_delta lets a plugin watch tokens as they arrive. See Hermes Agent streaming for how streams are timed and cut.
Hooks pair well with the best Hermes Agent plugins and Hermes cron jobs. For the full operating system I run around agents like Hermes, read the Agent OS guide. For how I test the models behind them, see Goldie Bench.
Want help mapping hooks onto your own business? Book a free AI strategy session and we'll look at where guardrails and automation fit.
Hermes agent hooks FAQ
Do Hermes agent hooks work in the CLI and in Telegram?
Plugin hooks and shell hooks work in both. Gateway event hooks run only in the gateway.
Can a hook stop the model from seeing a prompt?
Yes, on main since 1 November 2026. A pre_llm_call hook that returns an action of block with a message stops the turn before any inference request. The prompt is stored as a withheld placeholder, so later turns, compaction and memory providers never read it. It is not in a tagged release yet. The latest tag is v0.22.5.
What happens if my hook crashes?
Each hook type has its own rule. A crashed or timed-out pre_tool_call blocks the tool. A crashed pre_llm_call is skipped and the turn runs. Shell hooks fail open unless you set fail_closed on a pre_tool_call entry.
Where do I get help building hooks into a real workflow?
Join the AI Profit Boardroom for the playbooks, or book a free AI strategy session. If you want a done-with-you plan for your team, the strategy session is the place to start.
Set up one pre_tool_call guardrail today, then add a pre_llm_call block once it ships in a tagged release.











