Streaming Hermes outputs into AGNT heartbeat logs
Forward every Hermes routing decision as an AGNT heartbeat event.
Hermes is an open-source agent router that picks the right model + tool for each user turn. AGNT has its own heartbeat log — a structured stream of agent decisions replayable in the supervisor UI. This guide wires the two together so Hermes routing decisions become first-class heartbeat events.
Prerequisites
- A running Hermes deployment.
- Write access to AGNT's heartbeat ingest endpoint.
- Comfort with JSON stream forwarding in Node or Python.
Why forward at all
Hermes emits its routing decisions locally — logs, metrics, traces. Fine for debugging Hermes itself. Useless for understanding how your AGNT fleet behaved because AGNT's supervisor can't see the decision that led to the model call.
Forwarding the Hermes decision stream into AGNT's heartbeat makes the routing layer observable from the same place you observe the rest of the fleet. You can correlate "agent X chose model Y for task Z" with "agent X then ran the next 4 tool calls in this order" — all in a single replay.
The heartbeat event shape
AGNT heartbeats are JSON lines with a fixed envelope:
{
"timestamp": "2026-04-10T10:23:45Z",
"agent_id": "uuid",
"event_type": "routing_decision",
"payload": { "router": "hermes", "model": "claude-sonnet-4-6", "reason": "..." }
}For Hermes forwarding, `event_type` is `routing_decision` and `payload.router` is `hermes`. The rest of the payload mirrors Hermes's native decision record.
Step 1 — Subscribe to Hermes's decision stream
Hermes exposes its decisions via its metrics/trace API. The exact shape depends on your Hermes version, but the common pattern is an HTTP streaming endpoint that emits one JSON decision per line.
Pick your subscription point. If Hermes is deployed next to your AGNT fleet, use the internal address. If Hermes runs in a separate cluster, make sure the network path is authenticated — you don't want random clients reading your routing decisions.
Step 2 — Write the forwarder
A minimal forwarder in Node:
import fetch from "node-fetch";
const HERMES_STREAM = process.env.HERMES_STREAM_URL!;
const AGNT_INGEST = "https://api.agntdot.com/heartbeat/ingest";
const AGNT_KEY = process.env.AGNT_API_KEY!;
const res = await fetch(HERMES_STREAM);
const reader = res.body!.getReader();
const decoder = new TextDecoder();
let buffer = "";
while (true) {
const { value, done } = await reader.read();
if (done) break;
buffer += decoder.decode(value);
const lines = buffer.split("\n");
buffer = lines.pop() || "";
for (const line of lines) {
if (!line.trim()) continue;
const decision = JSON.parse(line);
const heartbeat = {
timestamp: decision.timestamp,
agent_id: decision.agent_id,
event_type: "routing_decision",
payload: { router: "hermes", ...decision },
};
await fetch(AGNT_INGEST, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${AGNT_KEY}`,
},
body: JSON.stringify(heartbeat),
});
}
}This is a minimum-viable forwarder. Production: add backpressure (don't flood the ingest endpoint), retries with exponential backoff on POST failures, and a dead-letter queue for events that fail validation.
Step 3 — Handle the heartbeat backpressure envelope
AGNT's heartbeat ingest enforces a rate limit per agent_id. High-frequency Hermes decisions (sub-second cadence) can blow past the limit if you forward every single decision. Two strategies:
- **Sample.** Forward every Nth decision. Loses granularity but keeps the stream under the limit.
- **Aggregate.** Batch decisions into a single heartbeat every N seconds, with the individual decisions in the payload array. Preserves granularity but adds complexity.
For most workloads, aggregation is the right choice. Batch 1-second windows: one heartbeat per agent per second, carrying all the routing decisions that happened in that window.
Step 4 — Replay in the supervisor UI
Once the heartbeats are flowing, AGNT's supervisor UI shows them inline in the agent's event timeline. You can filter by `event_type: routing_decision` and see exactly which model Hermes picked for every turn, alongside the tool calls that followed.
This is the payoff: the supervisor becomes a single pane of glass for routing + execution. When a task fails, you can see whether the model choice was correct and whether the subsequent tool calls matched the routing intent.