I wanted one simple task I could use to trace Hermes end to end.
Create a file with a unique marker. Read it. Calculate its SHA-256 digest. Then answer a follow-up:
Which marker did you read, and which workspace did you use?
The task is deliberately boring. That makes the architecture easier to see.
Now follow that same request through two Hermes entry paths: the terminal path and the messaging-gateway path.
Do they become the same agent?
Not automatically.
The terminal and the gateway start from different surfaces. They can converge on the same runtime machinery, provider resolution, tools, and persistence layer. But the session and routing identifiers still determine which conversation, workspace, and response destination the system owns.
That is the real question in Part 1:
Where does a message stop being input from a UI and become a stateful agent run?
Hermes is not persistent because one model process stays alive. It feels persistent because the system can reconstruct a run from explicit identities and durable state:
message → session → context → model/tool loop → persistence → delivery
Shared runtime does not mean shared conversation.
One task, two entry points
To make the trace concrete, I used a disposable directory and one synthetic file:
mkdir -p hermes_part1_lab
printf 'HERMES-P1-LAB-2026-08-02-7F3A\n' > hermes_part1_lab/task.txt
cat hermes_part1_lab/task.txt
sha256sum hermes_part1_lab/task.txtThe deterministic output is:
HERMES-P1-LAB-2026-08-02-7F3A
170e1f95d813a31cb4e9a79a1cfb39723b65f892e64db79e4607e42306f95166 hermes_part1_lab/task.txtThere is nothing agent-specific about that computation. That is the point.
If the work is deterministic, we can stop debating whether the answer “looks right” and focus on which system components must agree for the answer to belong to the correct conversation.
Start with the terminal.
A CLI-shaped request can create or resume a persisted session, resolve the configured provider, construct the agent runtime, and enter the model-tool loop. The normal CLI path does not first travel through the messaging gateway.
A messaging request takes a different route.
A platform adapter receives the platform event and normalizes it. The gateway runner then authorizes the source, resolves the session route, loads the relevant state, constructs or reuses the runtime, and eventually hands the final response back to the adapter for platform delivery.
The Hermes architecture overview shows CLI, gateway, Agent Client Protocol (ACP), API, batch, and Python entry points converging on the same core runtime.
That convergence is useful, but it does not merge their conversations.
A fresh CLI session and a fresh Telegram DM can use the same profile, provider resolver, model, tool registry, state database, and execution backends while still referring to two different conversations.
If you actually want continuity across surfaces, Hermes makes that explicit. The sessions guide describes resume by session ID or title and a /handoff path that can bind a messaging destination to an existing CLI session.
Conceptually, the mapping looks like this:
{
"destination_session_key": "agent:main:telegram:group:group-redacted:thread-redacted",
"session_id": "existing-session-redacted",
"automatic_cross_surface_merge": false,
"continuity": "explicit rebind"
}The important thing is not the exact string.
The important thing is that routing identity and conversation identity are separate objects.
The two paths start differently, but they eventually need the same things: a conversation to continue, a provider to call, tools to expose, state to persist, and somewhere to send the answer.
The interesting boundary is where the entry point stops owning that work.
The surface does not own the run
The CLI owns terminal interaction.
A messaging adapter owns platform-specific ingress and egress.
Agent Client Protocol owns its editor protocol.
Those are entry surfaces. They are not the entire agent.
The messaging gateway and the agent runtime also have different jobs.
The gateway layer owns long-running messaging concerns: normalized events, authorization, command routing, session routing, active-session behavior, restart recovery, and returning output to the correct platform adapter.
The Gateway Internals make that separation visible.
The agent loop owns the turn itself. It assembles the effective context and tool schemas, calls the model, parses tool requests, invokes tools, incorporates their results, handles retries or fallback, persists the completed turn, and returns final text.
The Agent Loop Internals show several provider-specific wire formats converging on one internal conversation model.
If you remember one picture from this post, make it this one:
The model is inside the loop.
It does not own the loop.
Provider resolution is a good example of why that distinction matters. The provider runtime decides things such as the selected provider, model, endpoint, credentials, and API mode. The model performs inference after those decisions have been made.
In v0.19.1, provider resolution starts with explicit runtime input, then saved configuration, then environment variables, then provider-specific defaults or automatic resolution. The same resolver is used across multiple Hermes entry points and auxiliary model operations. See the Provider Runtime Resolution.
That shared resolver reduces accidental differences between surfaces.
It still does not make the surfaces the same session.
“Remote execution” needs similar precision. In an agent system, remote can describe several different boundaries:
the model runs behind a remote provider API;
a tool executes through SSH or another remote backend;
the gateway itself runs on another machine.
Those are not interchangeable.
A remote model does not imply remote shell access. A remote execution backend does not merge conversation state. A remote gateway does not change which session owns a message.
The session owns continuity
Once a request has entered the system, the most important question becomes:
Which conversation does this event belong to?
Hermes uses several identifiers, and they solve different problems.
A session key is the gateway’s deterministic routing identity. It chooses the conversation lane for an inbound event.
A session ID identifies one persisted conversation incarnation. It tells the system which stored transcript and associated metadata to load.
A task ID can correlate work inside a run or tool-execution scope. It is not the durable identity of the conversation.
A parent session ID links one persisted session to an earlier session, for example when context compaction creates a continuation.
The session key chooses the lane. The session ID carries the conversation.
The session routing source shows that source metadata can carry platform, chat, thread, user, scope, and profile information, while the session key remains a logical routing identifier separate from the persisted session ID.
A gateway key can therefore include:
profile namespace
platform
chat type
chat ID
thread ID
platform scope where applicable
participant identity when the configured isolation policy requires it
That distinction matters.
A Slack workspace identifier is platform scope. It is not the filesystem workspace in which tools operate.
The routing model produces keys with shapes like these:
Telegram DM
agent:main:telegram:dm:chat-redacted
Telegram regular group, user A
agent:main:telegram:group:group-redacted:user-a
Telegram regular group, user B
agent:main:telegram:group:group-redacted:user-b
Telegram thread
agent:main:telegram:group:group-redacted:thread-redacted
Named profile
agent:research:telegram:dm:chat-redactedIn v0.19.1, ordinary group sessions are isolated by participant by default, while threaded sessions are shared by default unless per-user thread isolation is enabled.
That is a routing policy, not a universal security guarantee.
A collaborative engineering thread may intentionally share one conversation. A support inbox may need every sender or ticket to remain separate. The key design determines which semantics you get.
The durable state primarily lives in SQLite. The Session Storage describes ~/.hermes/state.db as the persistence layer for session metadata and message history. The broader architecture also identifies SQLite plus FTS5 as the session-storage substrate.
SQLite still permits one writer at a time. WAL allows readers to continue while writes commit, and Hermes adds application-level retries around write contention.
That is a database concurrency rule.
It is separate from the gateway’s semantic rule about whether two turns may mutate the same active conversation at the same time.
The ownership split looks like this:
That table also exposes another common category mistake:
session state is not prompt context.
The session is the durable record used to reconstruct continuity.
The model receives an assembled payload for one call. That payload can contain selected history, instructions, tool definitions, retrieved context, and other runtime state.
The stored session can therefore be larger than what the model sees on a particular invocation.
Long-term memory is different again. It is durable state intended to be reintroduced across turns or sessions. Session continuity does not mean the model updated its weights, and it does not mean the complete transcript became long-term memory.
Resume is similarly mechanical.
A process does not need to stay alive for the conversation to continue. The system reloads stored state.
The CLI can also associate a session with a working directory. If that directory moves or disappears, the conversation may still exist while the execution workspace no longer does.
That is a real systems boundary:
correct transcript, wrong workspace can still produce the wrong action.
Restart recovery introduces another state transition. In the session implementation, resume_pending preserves the existing session ID after an interrupted gateway restart, subject to a freshness window, instead of silently creating a different conversation.
Freshness checks matter because old interrupted work should not resume indefinitely just because another message arrived.
Compression has a related continuity problem.
When a long session is compacted, Hermes can create a child session linked to the previous one and advance the active conversation toward that continuation.
The lineage remains inspectable.
The active context does not remain byte-for-byte identical.
That distinction belongs mostly in Part 2, but the Part 1 implication is important:
continuity can survive even when the active representation changes.
Inside one model-tool turn
Now the entry point and session are resolved.
What actually happens inside the turn?
At a high level:
accept the normalized user event;
resolve the session key and current session ID;
load history, workspace metadata, and safe session overrides;
resolve provider, model, endpoint, credentials, and API mode;
assemble the effective context and available tool schemas;
call the model;
parse final text or tool calls;
execute eligible tools;
append tool results to the working conversation;
call the model again if more reasoning or actions are needed;
persist the successful turn and usage metadata;
return final text to the entry surface;
for covered gateway replies, attempt platform delivery.
The architecture describes this same broad path: prompt construction, provider resolution, model invocation, tool dispatch, persistence, and response delivery around the core agent loop.
The reasoning, action, observation shape resembles the general loop described in ReAct.
The interesting engineering begins around it.
A tool schema tells the model how to request a capability.
It does not prove that the caller is authorized to use that capability.
It does not prove that the execution backend is isolated.
It does not prove that a destructive action has been approved.
Those guarantees belong elsewhere in the runtime.
Tool parallelism has another subtle boundary.
Hermes can execute multiple eligible non-interactive tool calls concurrently and then restore their tool-result messages in model-call order.
That keeps the conversation structurally valid.
It does not guarantee that external side effects occurred in that same order.
Fallback also changes more than a model name.
If the primary provider hits an authentication failure, rate limit, or server error, fallback can alter the provider, model, endpoint, client, or API mode.
Operational evidence should therefore record which provider actually served the call, not only which provider was selected at the beginning of the session.
The gateway version of one complete message looks like this:
No private chain-of-thought is required to understand this execution.
The architecture is visible through operational artifacts:
normalized source metadata
session key
session ID
provider and model
tool calls
tool results
transcript rows
workspace metadata
delivery state
That is enough to reconstruct what the system did.
Where it breaks
“The agent succeeded” is too vague to operate.
A more useful completion model is:
event accepted
→ run owned
→ external action completed
→ transcript committed
→ delivery obligation recorded when enabled
→ platform send attempted
→ platform reports success or ambiguity
→ reply becomes availableEach arrow is a separate failure boundary.
This becomes especially important when tools have side effects.
Suppose Hermes updates a file or calls an external API successfully.
The tool succeeded.
Then the transcript persists.
Persistence succeeded.
Then the gateway crashes during platform delivery.
The user sees no answer.
Calling the whole operation “failed” can cause an operator to rerun the task and duplicate the external action.
That is why execution, persistence, and delivery need separate evidence.
On the final-text gateway path covered by the delivery ledger, the durable response obligation and the platform send are separate states. The Gateway Internals likewise separates final-response delivery from the agent loop itself.
When the delivery ledger is active, the adapter can record the outgoing response as an obligation around the send.
The delivery state distinguishes outcomes such as:
pending
attempting
delivered
failed
abandonedIf the send never started, startup recovery may retry it normally.
If the process died after the send began, the transport result can be ambiguous. The platform may already have accepted the response.
The delivery-ledger implementation and regression tests model that recovery explicitly.
That is an at-least-once recovery problem.
It is not exactly-once delivery.
Streaming output, progress messages, media, explicit tool-driven sends, and other outbound paths may have different semantics. A final-text delivery ledger should not be generalized into “every byte Hermes sends is durable.”
A tool result is not a delivered answer.
At 3:00 AM, I would split the investigation into these six cases:
The common pattern is ownership.
A routing bug is not repaired by changing the model.
A provider problem is not repaired by changing the chat ID.
A delivery problem should not cause a destructive tool to run twice.
What I would steal
I would steal four patterns from this architecture.
Separate routing identity from conversation identity.
One identifier decides which lane receives an event. Another identifies the durable conversation. That makes reset, resume, compression, and handoff explicit instead of magical.
Make busy behavior a named policy.
Interrupt, steer, and queue mean different things to a user. Decide which contract applies and preserve message boundaries.
Persist intent, re-resolve authority.
A session can remember non-secret runtime intent such as the selected provider or model. Credentials should still be resolved through the normal authentication path.
Treat delivery as an obligation, not a log line.
A response can exist before the user has received it. Track that state explicitly when the transport and product path warrant it.
Builder checklist
Define exactly which fields form your routing key.
Keep routing identity separate from durable conversation identity.
State whether your single-writer guarantee is process-local or distributed.
Define what a second message does during an active run.
Persist workspace and provider intent without casually persisting credentials.
Keep session lineage inspectable when context is compacted.
Record tool effects, transcript commits, and delivery outcomes separately.
Define recovery semantics before claiming exactly-once behavior.
Try it yourself
Pin one Hermes release or commit.
Create a disposable profile, repository, and synthetic file.
Configure the narrowest toolset and permissions needed for the task.
Run one deterministic task and record the session ID, workspace, provider, and visible tool evidence.
Resume the session and confirm that the expected conversation and workspace return.
Compare a second entry point using explicit handoff, a non-production gateway, or an official test harness.
Copy
state.db, inspect its schema first withPRAGMA, and only then query sanitized routing or session metadata.Induce one safe restart or delivery interruption in a disposable environment, inspect recovery, then delete the test artifacts.
The main takeaway is simple.
Hermes does not need one immortal process to feel continuous.
The entry point starts the request.
The session owns the conversation.
The runtime owns the turn.
The tool runtime owns execution.
Persistence records what happened.
On gateway paths, the adapter owns the final platform send.
That chain is what turns a message into persistent work.
Part 2 will examine the next question: once Hermes knows which session owns the turn, how does it decide what the model actually sees?
That means prompt assembly, project context, volatile state, caching, and compression.
Where does continuity break first in your agent stack: session identity, provider resolution, workspace ownership, persistence, or delivery?
Subscribe to follow Hermes Agent Architecture Part 2: Prompt Assembly, Context Files, and Compression.
Hermes Agent Architecture Series
Part 1: Gateway, Sessions, and the Agent Loop
Part 2: Prompt Assembly, Context Files, and Compression
Part 3: Memory, Skills, and the Self-Improvement Loop
Part 4: Tools, Plugins, Delegation, and Persistent Work
Part 5: Security Boundaries, Profiles, and Safe Deployment
Version note
Researched August 2, 2026 against Hermes Agent v0.19.1, tag v2026.7.30, commit cc4cab2, on Linux x86_64. Architecture claims were checked against the source, official documentation, and regression tests, with deterministic local checks for the state and routing artifacts used in this post. Later releases may behave differently.
References
Hermes Agent v0.19.1, the release used as the version boundary for this post.
Hermes Agent Architecture, the high-level map of entry points, runtime, tools, and persistence.
Gateway Internals, the messaging ingress, authorization, active-session handling, and routing boundary.
Session Storage and Sessions, for persisted state, workspace metadata, lineage, resume, and handoff.
Agent Loop Internals and Provider Runtime Resolution, for model invocation, tool execution, provider modes, retries, and fallback.
Session Routing and Restart Recovery and the restart-recovery tests, for routing identity and interrupted-session recovery.
Delivery Ledger and Recovery Tests, together with the delivery-ledger regression tests, for final-response delivery state and bounded recovery.
ReAct: Synergizing Reasoning and Acting in Language Models, for the general reasoning, action, and observation loop used as architectural context.





