TokenWatch for Hermes Agent
TokenWatch is a Hermes general plugin that monitors LLM token usage and can freeze subsequent tool/gateway activity after configured limits are reached.
It tracks:
- input tokens sent to the model
- output tokens received from the model
- total tokens
- cache read/write and reasoning tokens when Hermes/provider usage includes them
Install
Run the installer from this repository:
python install.py --interactive
The installer copies the plugin to ~/.hermes/plugins/tokenwatch, enables it in ~/.hermes/config.yaml, and writes your TokenWatch settings.
After publishing the package, the one-shot terminal flow is:
uvx hermes-tokenwatch --interactive
Or with pipx:
pipx run hermes-tokenwatch --interactive
During development from this repo:
pipx run --spec . tokenwatch --interactive
For scripted setup:
python install.py \
--scope session \
--limit-total-tokens 100000 \
--warning-levels 50,80,95 \
--freeze \
--notification-channel "" \
--webhook-url ""
You can still install manually by copying this directory to one of Hermes' plugin locations:
mkdir -p ~/.hermes/plugins/tokenwatch
cp -R . ~/.hermes/plugins/tokenwatch
hermes plugins enable tokenwatch
For project-local installation, put it at .hermes/plugins/tokenwatch and start Hermes with HERMES_ENABLE_PROJECT_PLUGINS=true.
Configure
Add the plugin to ~/.hermes/config.yaml:
plugins:
enabled:
- tokenwatch
entries:
tokenwatch:
scope: session # session, daily, or all_time
limit_total_tokens: 100000
limit_input_tokens: 0 # 0 disables this limit
limit_output_tokens: 0
warning_levels: [0.5, 0.8, 0.95]
freeze_on_limit: true
warn_on_predicted_limit: true
notify_console: true
notify_gateway: true
notification_channel: "" # blank uses the active/default Hermes channel
notification_platform: "" # optional platform name when channel is set
notify_webhook_url: ""
inject_cli_interrupt_on_freeze: true
interrupt_gateway_on_freeze: true
freeze_on_limit blocks subsequent tool calls with Hermes' pre_tool_call block directive and skips new gateway dispatches with pre_gateway_dispatch. Current Hermes does not honor a blocking return value from pre_api_request, so the plugin cannot cancel the exact LLM request that crosses a budget before it is sent. If a model response already contains tool calls when the limit trips, Hermes may still make another LLM call after the blocked tool result unless the runtime interrupt is processed first. TokenWatch keeps freeze notifications one-shot to avoid alert loops, and in CLI mode it can inject an interrupt message to stop the active run as soon as Hermes can process it.
Disabling the plugin after a trip is not recommended: it would remove the guardrails and let spending continue. Use /tokenwatch unfreeze or hermes tokenwatch unfreeze after you intentionally raise/reset the budget.
When interrupt_gateway_on_freeze is enabled, TokenWatch also asks the active Hermes gateway session to stop through the same internal path used by /stop: interrupt the running agent, invalidate the active run generation, clear pending messages, and release the running-session lock. This is best-effort and only applies when Hermes is running under a gateway platform.
Commands
Inside a Hermes session:
/tokenwatch status
/tokenwatch reset
/tokenwatch unfreeze
From the terminal:
hermes tokenwatch status
hermes tokenwatch reset
hermes tokenwatch unfreeze
Notification Delivery
By default, TokenWatch sends warnings/freezes back through Hermes' active gateway channel when Hermes is running under a gateway platform. Leave notification_channel blank to use the channel that triggered the active run, falling back to the platform home channel when Hermes exposes one.
To force delivery to a specific destination, set notification_channel to that platform's chat/channel ID. If more than one gateway platform is connected, also set notification_platform, for example discord, slack, telegram, or teams.
notify_webhook_url is only for external HTTP delivery. Leave it blank unless you want TokenWatch to POST JSON to a separate service.
If notify_webhook_url is set, TokenWatch sends a JSON POST with:
{
"plugin": "tokenwatch",
"level": "warning",
"message": "TokenWatch total usage is 80,000/100,000 (80%).",
"scope": "session",
"session_id": "...",
"model": "...",
"provider": "...",
"platform": "...",
"timestamp": "2026-05-12T16:00:00+00:00"
}