Skip to content

Hermes: the plugins and their hooks

Skills are text: they steer the model. Plugins are Python code: they run, whatever the model decides.

A plugin registers on hooks of the Hermes lifecycle and can, depending on the hook, observe an event, rewrite a message before processing, or expose new tools to the agent.

PluginVersionContributionAuthor
telegram-voice-transcriptor1.1.0pre_gateway_dispatch hookCustom
telegram-message-editor1.0.0telegram_status_message toolCustom
codex-usage-alert1.0.0post_api_request hookCustom
mnemosyne0.4.020 tools + 3 hooksThird party (Abdias J)
HookMomentCan do
pre_gateway_dispatchMessage received, before processingRewrite or redirect the event
pre_llm_callBefore the model callInject context into the prompt
on_session_startSession openingLoad an initial state
post_tool_callAfter a tool callObserve, capture
post_api_requestAfter a successful LLM callObserve, trigger a side effect

Why user plugins rather than patching the core?

Section titled “Why user plugins rather than patching the core?”

The application tree /opt/hermes belongs to the image. A change there survives until the next update — and then disappears without a sound, which is the worst possible ending for a customisation.

User plugins live in /opt/data/plugins/, that is, in the bind mount. They survive docker compose up --force-recreate as well as image updates, and go into the daily backup.

It is the same rule as Mnemosyne’s wrapper mode: everything meant to last lives in the volume, never in the image.

Both add capabilities, but not in the same place nor at the same price.

N8N MCP toolPlugin
WhereA node in a workflowPython inside the container
AccessWhatever N8N can reachThe Hermes runtime itself
ChangeEdit the workflow, restart the containerEdit the file, restart the container
Can react to an eventNo — only be calledYes, through hooks

The dividing line is simple: a business action becomes an MCP tool; a behaviour that must fire without the agent deciding becomes a plugin.


On a Telegram voice message, the plugin rewrites the event to inject the block of the transcriptor-fr-voice skill. The STT pipeline then prefixes the transcript as a quote, and the agent replies with the cleaned-up French text.

Its interest lies less in what it does than in what it refuses to fire on.

The hook is also fail-open: any exception is logged as a warning and the message goes on to normal processing. A broken skill or a failed import must never swallow a voice message.

This plugin exposes one tool, telegram_status_message, with two actions: create sends a message and returns its message_id, update edits that same message in place. 4096-character cap, and by default the current conversation when the call comes from Telegram.

The problem it solves is an ergonomics problem. An agent working for several minutes on a multi-step task has a choice between staying silent — and looking stuck — or sending one message per step, and drowning the conversation. In-place editing gives a third option: a single dashboard that updates itself.

Hermes shares the ChatGPT subscription with CLI Ollama and the N8N workflows. Hitting the weekly cap produces no signal until the bot starts answering with errors.

The plugin registers on post_api_request, reacts only to openai-codex provider calls, and alerts when account consumption crosses a threshold (90% by default).

Its four guardrails are worth noting, because they are what separates an acceptable observation plugin from one that degrades the service:

GuardrailImplementation
Non-blockingThe hook starts at most one short daemon thread and hands control back immediately
Controlled loadA 300 s cooldown avoids calling the backend again on every message of an active conversation
Anti-spamOne alert per threshold + quota window + reset time combination, with history bounded to 200 keys
Fail-openAny error is logged and never affects the Hermes response

The common principle: a plugin grafted onto a hot path must never be what breaks or slows the response.

The only third-party plugin of the set, and by far the largest: 20 tools and 3 hooks (pre_llm_call, on_session_start, post_tool_call). It is installed in wrapper mode and has a dedicated article.

Its presence in this list says something about the extension system: long-term memory, arguably the agent’s most structuring feature, installs through the same mechanism as a quota alert.

A plugin is a directory with a plugin.yaml and an __init__.py exposing register(ctx).

def register(ctx) -> None:
ctx.register_hook("post_api_request", _on_post_api_request)
name: codex-usage-alert
version: 1.0.0
description: "Non-blocking Telegram Home alert when OpenAI Codex account usage crosses a threshold."
author: Guillaume PARRAT + Hermes Agent
provides_hooks:
- post_api_request

Only telegram-voice-transcriptor has versioned source in the repository; deployment happens by copy then restart:

Fenêtre de terminal
cp -r ai-stack/hermes/plugins/telegram-voice-transcriptor ai-stack/hermes/data/plugins/
docker compose -f ai-stack/docker-compose.yaml restart hermes
docker exec hermes hermes plugins list # expected status: enabled

LimitImpactMitigation
Three plugins out of four unversionedOnly the transcriptor has source in the repository; the others exist only in productionDaily backup; porting to the repository still to do
Restart requiredAny change forces a container restartBatch the changes
No automated testA broken plugin is discovered in useFail-open limits the damage to one lost feature
Hooks unversioned upstreamAn upstream signature change breaks a pluginhermes plugins list after every image update
No isolationA plugin runs inside the gateway processDiscipline: non-blocking and fail-open by default

If plugins must be versioned:

  • ai-stack/hermes/plugins/ already exists and holds the transcriptor — the other three follow the same pattern.
  • Main benefit: being able to roll back after an upstream update that breaks a hook.

If a plugin must become blocking:

  • The typical case would be a real approval chain before sensitive actions, rather than the current system prompt rule.
  • A pre_tool_call hook would fit — but a blocking plugin on the hot path needs an explicit timeout and fallback behaviour, otherwise a plugin failure freezes the agent.

If observability must improve:

  • post_api_request and post_tool_call are the natural points to export metrics.
  • A plugin pushing a counter to Prometheus would give Hermes the same visibility as the rest of the stacks in Grafana.

  • Hermes Agent — The deployment and plugin activation
  • Mnemosyne memory — The largest plugin of the set
  • Skillshermes-user-plugins and hermes-runtime-plugins, the writing toolkit
  • MCP tools — The other way to add a capability