Skip to content

Hook Pipeline

The plugin registers 12 Hermes hooks. Here is what each one does and why it was chosen:

Hook Purpose
────────────────────────────────────────────────────────────────────────────
on_session_start Create runs row, extract cron_job_id from session_id.
Fired ONCE per new session (not per turn).
pre_api_request Stash approx_input_tokens keyed by (session_id, call_count)
for the fallback estimator when usage=None.
post_api_request PRIMARY TOKEN SOURCE. One call per individual API call
within a turn. Carries the usage dict with real token
counts. Calculates cost, records llm_calls row (incl.
turn_id / task_id / api_request_id, schema v18), updates
runs totals.
api_request_error (1) Records EVERY API failure per attempt in the
llm_call_errors ledger — any status, retryable or not
(schema v18). (2) Captures 404 "model removed /
unavailable" errors to power the model-unavailable
alert. NO token data.
post_tool_call Records tool name, success/failure, latency, and (v18)
the turn_id + tool_call_id it belongs to. Also records
a proxy row for delegate_task/subagent calls (no token
data there, just a count).
post_llm_call Fires once per turn after the tool loop. NO token data.
Used only to keep runs.ended_at current during multi-turn
interactive sessions.
subagent_start Records the parent→child delegation edge (subagent_edges)
so subagent cost attributes to the root cron_job. Fires
synchronously before the child dispatches. NO token data.
subagent_stop Records a synthetic tool_call row ("delegate_task/subagent")
for proxy count of subagent invocations, and finalizes the
subagent_edges row (stopped_at + child_status). NO token data.
on_session_end Fires at the end of every run_conversation() call. Sets
final status: ok / error / interrupted.
on_session_finalize Safety net for true session teardown (CLI atexit, gateway
expiry). Ensures status is "ok" if not already set.
pre_llm_call (1) Attaches sender_id to the run for per-sender budgets.
(2) Injects one-time-per-window soft budget alert into
the conversation context (`enforce` mode only).
(3) Injects one-shot free→paid transition warning when
the current model was previously seen as free but is
now incurring cost.
pre_tool_call Hard budget enforcement. Returns {"action":"block",...}
if any scope is in hard breach. Also triggers cron pause.
Only registered in `enforce` mode (the default); in
`observe` mode the hook is never registered.

Why post_api_request is the primary token hook: Hermes can make multiple API calls per turn (retries, tool-call rounds, reasoning). Only post_api_request fires per individual API call and carries the usage dict. The pre_/post_llm_call hooks fire once per turn and have no token data. This design was discovered from the source (not guessable from the hook names alone).

Why not capture budget in post_api_request: Token counts have just been recorded but verdict cache is stale. The tool-gate in pre_tool_call is the right place — it fires synchronously before the next tool and has a fresh view of accumulated spend.

hermes-telemetry is an independent community plugin for Hermes Agent. It is not affiliated with or endorsed by Nous Research.