Skip to content

Hermes Agent Integration

The Hermes integration turns a self-hosted Hermes Agent (Nous Research’s autonomous AI agent) into a first-class DonutChat bot that behaves like a Slack-thread bot. A user @mentions the bot (or DMs it), and Hermes answers in a thread under that message — the channel stays clean, the answer streams in live, and follow-up questions inside the thread continue the same conversation.

A small standalone service, hermes-bridge, does the wiring: it consumes the DonutChat bot event stream, forwards each triggering message to Hermes’s OpenAI-compatible API server using one session per thread, and streams the reply back through the bot thread-reply API.

What you runA Hermes Agent box + the hermes-bridge service
Where it answersIn a thread under the triggering message (Slack-style)
Bot transportDonutChat bot WebSocket stream in, bot thread-reply API out
Agent transportOpenAI-compatible POST /v1/chat/completions with stream: true
Session modelOne Hermes session per thread (X-Hermes-Session-Id = donutchat:<chat_id>:<parent_message_id>)
RepliesStream progressively (GNOK) so the thread fills in as the agent thinks
CouplingNone — the bridge talks to the same public bot APIs any third-party bot uses
BackendAny OpenAI-compatible endpoint, not just Hermes
flowchart LR
U["User @mentions the bot"]

subgraph Backend["DonutChat backend"]
  ED["event_dispatcher (message.new / thread.reply.new)"]
  BH["bot_hub WebSocket fan-out"]
  TR["bot thread-reply API"]
  TH["ThreadHandler: ThreadReply + streaming"]
  TP["ThreadPresence viewer gating"]
end

subgraph Bridge["hermes-bridge"]
  W["per-thread worker (serial per thread, parallel across threads)"]
  CB["circuit breaker"]
end

API["Hermes OpenAI-compatible API :8642"]

U -->|posts message| ED
ED --> BH
BH -->|message.new / thread.reply.new| W
W -->|"POST /v1/chat/completions (stream, session donutchat:chat:parent)"| API
API -.->|SSE deltas| W
W -->|"create GN reply, stream deltas, finalize OK"| TR
TR --> TH
TH --> TP
TP -->|ThreadReplyContentEvent| U
CB -.->|guards| W
hermes-bridge connects the DonutChat bot APIs to a Hermes Agent, one session per thread
  1. A user posts a message that triggers the bot (an @mention, or any message when the bot’s trigger mode is all).
  2. DonutChat pushes a message.new event (or, for a reply inside an existing thread, a thread.reply.new event) over the bot WebSocket stream.
  3. The bridge routes it to a per-thread worker — events in one thread are processed in order, while different threads run in parallel.
  4. The worker opens a streaming Hermes completion with the thread’s session id, then creates a “generating” placeholder reply in the thread so users see Hermes is working.
  5. As Hermes streams tokens, the bridge pushes them to the thread (status: GN); when the agent finishes it finalizes the reply (status: OK). Clients viewing the thread see the answer type out via ThreadReplyContentEvent — the same streaming path AI Direct Answers already use.
sequenceDiagram
actor User
participant DC as DonutChat backend
participant BR as hermes-bridge
participant HM as Hermes Agent

User->>DC: @hermes what is the weather?
DC-->>BR: message.new (message_id=100, mentions_bot)
BR->>HM: POST /v1/chat/completions stream, session donutchat:chat:100
BR->>DC: create thread reply under 100 (status GN)
DC-->>User: thread reply appears (Hermes is thinking)
loop streaming
  HM-->>BR: SSE token delta
  BR->>DC: stream content (status GN)
  DC-->>User: ThreadReplyContentEvent (live text)
end
HM-->>BR: DONE
BR->>DC: finalize reply (status OK)
User->>DC: in-thread follow-up
DC-->>BR: thread.reply.new (parent=100)
BR->>HM: same session donutchat:chat:100
Note over BR,HM: thread context preserved
An @mention opens a thread; the reply streams in; follow-ups continue the same session

Threads keep an AI agent from flooding the channel: the answer (which can be long, and streams over many seconds for tool use) lives under the triggering message, and a back-and-forth with the agent stays in one place — exactly how Slack scopes app conversations. Each thread maps to an independent Hermes session, so two parallel questions never cross context.

OptionTrade-off
Bridge service (chosen)Works with today’s public bot APIs, zero coupling, reusable for any OpenAI-compatible backend. Cost: one more deployable.
Native Hermes gateway adapterFirst-class (Hermes session store, cron, full toolset) but requires an upstream contribution and ongoing maintenance in a foreign repo. A possible future addition.
Hermes webhook toolsetRejected — its webhooks grant Hermes full tool access (including terminal) per request, the wrong trust model for untrusted chat input.
CapabilityStatus
@mention / trigger-mode replies, answered in a thread
Streaming replies (progressive GNOK)
Follow-up turns inside the thread (per-thread Hermes session)
1:1 DM with a bot
BOT badge + markdown rendering on thread replies
Graceful “agent unavailable” fallback when Hermes is down✅ (circuit breaker)
Bot directory, interactive components⏳ future