Hermes Agent's Durability Rule: Persist Before a Tool Can Act

Cover Image for Hermes Agent's Durability Rule: Persist Before a Tool Can Act

Listen with Article TTS Reader

Checking for Article TTS Reader…

Hermes Agent is a Python harness with a very practical priority: when an agent performs a side effect, its intent should already exist in durable history. The main conversation loop is large and tightly integrated, yet this one ordering rule gives the system an unusually clear answer to a common failure case.

This is the sixth article in the Agent Harness series. I reviewed Nous Research's Hermes Agent 0.20.5 at upstream revision 5b82658b3cca. For the channel-facing predecessor, see OpenClaw's agent gateway.

The loop treats iterations as a budget

Hermes runs its conversation loop while both an API-call limit and a thread-safe IterationBudget permit it, with a grace-call path for a final response. The budget is a consume-and-refund counter rather than a plain turn number.

The defaults illustrate why that distinction matters. A parent agent's maximum turns are effectively unbounded through an internal large sentinel, while delegated subagents default to 250 iterations. A tool batch receives a budget refund only when every call in that batch is execute_code; the API-call count still rises, and a mixed batch does not receive the refund.

This treats agent progress as an allocatable resource. It does not redefine every code execution as a free turn. Hermes also records an explicit exit reason for each turn, covering ordinary completion, interruption, budget exhaustion, persistence failure, truncated responses, and other cases. Investigating why work stopped becomes a state query rather than a log archaeology exercise.

Persist the intent before the side effect

When an assistant message includes tool calls, Hermes first writes that message to SQLite. Only a successful write allows the tool dispatcher to run. If persistence fails, the turn exits with session_persistence_failed and no tool from that message executes. Tool results are then persisted incrementally as they arrive.

The rationale is easy to test mentally. If a process deletes a directory and crashes before its transcript is saved, the filesystem has changed without a record of the model's decision. Reversing the order means every completed side effect has a durable record of the assistant message that requested it.

The guarantee is about ordering, not rollback. A saved record does not make an external action reversible. It ensures an interruption cannot leave the system claiming that an action never happened simply because the process died before writing its history.

Tool handling protects the protocol as well as the process

Tool calls pass through registry dispatch. Exceptions are normalized into JSON error results, keeping failures inside the conversational protocol. Hermes also coerces string arguments according to the declared schema, which addresses a routine model failure where numeric values arrive as strings.

Concurrent calls use a thread pool, then results are returned in the original model-requested order. Before dispatch, a middleware sequence can rewrite a call through Relay, run plugin pre_tool_call hooks, and apply guardrails.

The loop also repairs protocol damage around abnormal exits. It synthesizes error results for unanswered tool calls, handles empty responses, and removes trailing scaffolding that would violate role alternation. Those repairs are important around user interruptions, where apparently harmless queueing can produce message sequences that an API will reject.

Stop, redirect, and steer have different meanings

Hermes uses a background thread for a synchronous HTTP model request while the main thread watches for interrupts. It gives three user actions deliberately different semantics:

  • /stop hard-interrupts the active request and closes the worker transport.
  • redirect() cancels a normal model request, preserves completed history, and supplies a correction. During tool execution, it falls back to steering behavior.
  • /steer does not interrupt the active request or tool call. It queues the instruction until the current tool batch finishes, then attaches it after the final tool result.

The boundaries are conservative. A correction can take effect quickly without claiming that work already running has ceased safely.

SQLite keeps old context recoverable

Hermes stores sessions in SQLite using WAL mode and FTS5 full-text search. Its messages table separates api_content, the model-facing representation, from human-facing display fields. Reasoning content and compaction summaries can therefore be represented without forcing one view to serve both readers.

Context management has several layers. A preflight threshold begins at 50 percent of the configured context window and rises to at least 75 percent for models with windows below 512K. A second post-tool check uses the provider's actual prompt-token count.

By default, compression remains within the same session ID. Older rows become soft-archived with active=0; a new summary and recent tail form the live model view. The prior rows remain searchable through FTS5 and can be restored. A legacy configuration can rotate into child sessions with a parent_session_id, and an optional, disabled-by-default micro-compaction mechanism spreads the cost of summarizing old exchanges across turns.

This is non-destructive compaction. The model's context changes while the older physical record remains available for inspection and recovery.

Terminal controls are layered, with distinct boundaries

Hermes offers seven terminal environment implementations: local, Docker, Modal, Vercel Sandbox, SSH, Singularity, and Daytona. Selecting an isolated backend changes the execution environment, while command approval adds a separate policy layer.

The approval logic detects hard-line commands such as recursive deletion, protects sudo input, supports user deny rules and glob-based persistent allowlists, and rejects compound shell operators that could bypass those rules. Its result depends on the environment and mode: an isolated backend can bypass the host-danger guard; hard-line commands and user denials block directly; non-interactive mode can approve automatically; Gateway and ask workflows may return pending_approval. There is no single universal meaning for "not yet approved."

Hermes also wraps longer textual output from high-risk web, browser, and MCP tools in untrusted-data delimiters as a defense against indirect prompt injection. The scope is intentionally limited: short text, non-text values, and some multimodal content pass through unchanged. It is a targeted architectural boundary, not proof that every tool result is safe.

The durable record is the design center

Hermes combines budgets, interruption semantics, protocol repair, recoverable compaction, and terminal controls for long-running autonomous work. Its code organization is less plugin-oriented than several other harnesses in this series, yet the persistence-before-side-effects rule is a broadly useful reliability principle: if an agent is about to change the world, make its intent durable first.

Source scope

The implementation details in this article refer to upstream Nous Research Hermes Agent 0.20.5 at revision 5b82658b3cca, including its conversation loop, session database, compression, terminal approval, and tool dispatch code. Return to OpenClaw's persistent gateway architecture.