hermes is a runtime Clawboo drives by spawning the hermes CLI head-less, one run per task. What sets it apart from the other wrapped CLI runtimes is its persistent per-identity home: the agent’s self-created skills, MEMORY.md, sessions, and state.db accrue in one stable directory and compound across runs, so a Hermes teammate keeps learning instead of starting fresh each dispatch.
Clawboo takes zero Python and zero kanban dependency on Hermes. The adapter is lean TypeScript that drives the hermes binary as a black box. Hermes ships its own internal kanban; Clawboo never syncs it. Hermes reaches the team’s coordination surface by attaching Clawboo’s MCP servers (Tasks/Memory/Tools/TeamChat), not by exporting any state into Clawboo. Only a user who opts into the Hermes runtime installs Python.
If you only need the install/connect/disconnect mechanics shared across runtimes, see Connecting runtimes. This page covers Hermes’s capabilities, its specific connect flow, the driver, and the native-preservation invariants you must not break.
Capabilities
HermesAdapter.capabilities() declares the following. Two facts drive most of Hermes’s behavior: it is non-streaming, and it is a persistent-home wrapped one-shot.
The
nativeHome/nativeSkills/nativeMemory claims are what route Hermes to a persistent home by construction. The runner reads resolveRuntimeIntegration(capabilities()); it branches on the resolved integration plan, never on the runtime id, and a { persist: true, scope: 'per-identity' } claim resolves to a persistent home, materialized once per (runtime, identity) under Clawboo’s state dir.
streaming: false is a permanent runtime asymmetry, not a limitation Clawboo papers over. Quiet-mode hermes chat prints the final response, and the driver synthesizes a terminal done event on process exit. The board round-trip works the same as a streaming runtime; the difference is the absence of incremental text deltas in the team chat narration.Prerequisites
Hermes installs through Python.
pipx is preferred; a pip --user fallback applies if pipx is absent. You need Python 3.11 or newer (hermes-agent requires >=3.11,<3.14) with pip/pipx available. A present-but-too-old interpreter is its own failure (PYTHON_TOO_OLD), not PYTHON_MISSING; the installer prefers a python3.13/3.12/3.11 binary over a bare python3 so a fresh macOS default does not trip it.- An OpenRouter API key — Hermes’s
authKindisapi-keyand its vault slot isOPENROUTER_API_KEY— or a ChatGPT subscription (no key: see Or: use your ChatGPT subscription). (If your seeded Hermes config already names a provider, that provider wins and the key for it is what matters; see Provider precedence.) - The Clawboo server running. Connect, install, and run all go through
/api/runtimes/*.
Steps
1. Install the Hermes CLI
From the Runtimes panel, the Hermes card opensPOST /api/runtimes/hermes/install (a Server-Sent Events stream) from the not-installed state. Because Hermes’s packageManager is pip, the installer:
- Prefers
pipx install 'hermes-agent[anthropic]<1'. - Falls back to
python -m pip install --user 'hermes-agent[anthropic]<1'whenpipxis absent. - Retries once with
--break-system-packagesif it detects a PEP-668 externally-managed environment. - Emits an
errorevent with codePYTHON_MISSINGif neitherpipxnorpythonis found, orPYTHON_TOO_OLDwhen the interpreter it finds is older than 3.11.
hermes-agent[anthropic]<1 to block an auto-install of a future incompatible 1.0 major. The [anthropic] extra is load-bearing, not decoration: openai and openrouter ship with the Hermes core, but --provider anthropic fails with “the ‘anthropic’ package is required” without it.
2. Connect the OpenRouter key
POST /api/runtimes/hermes/connect with body { apiKey } stores the key in the encrypted vault under OPENROUTER_API_KEY and returns the recomputed connectionState (ready on success). The key is trimmed; a blank key returns 400 ({ error: "apiKey is required" }). The response never echoes the key.
Or: use your ChatGPT subscription
Hermes has a nativeopenai-codex provider that runs on a ChatGPT subscription — the same backend the Codex CLI uses, no API key anywhere. Once the subscription is connected (the ChatGPT row on the Providers page, or the wizard’s OpenAI card), the Hermes card offers an optional one-click Use my ChatGPT subscription that runs Hermes’s own login for you and shows the one-time code right in the UI. Or run it yourself in a terminal:
~/.hermes/auth.json; token values are never read into responses or logs) and the card reads Connected. In the team-creation model picker, Hermes gains a ChatGPT subscription group (GPT-5.5, GPT-5.4, GPT-5.4 mini, GPT-5.3 Codex, and the Pro-plan Spark); a pick pins --provider openai-codex with the bare backend model id.
Either way, the OAuth exchange runs entirely inside the official Hermes CLI and you authorize in your own browser on openai.com — Clawboo only relays the user-facing code and never touches tokens. Prefer this fresh sign-in over Hermes’s offer to import your Codex CLI credentials: each tool holding its own OAuth grant keeps refresh-token lineages separate (an imported token family shared across tools is how “refresh token reused” conflicts happen). Also note the subscription quota is shared — Codex-runtime agents, Hermes subscription runs, and your own
codex usage all draw from the same ChatGPT plan allowance, and these runs report no USD cost (budgets see estimated/zero spend).3. Run a board task on Hermes
POST /api/runtimes/hermes/run with a taskId drives the task end to end through the server-side executor runner: claim → worktree → run → report-up. At run time the connected key is resolved from the vault (resolveRuntimeKey over OPENROUTER_API_KEY) and injected into the spawned Hermes process’s environment. See the Runtimes API for the full request/response shape.
How the driver works
The real driver (createHermesDriver) spawns hermes chat head-less and builds a deterministic argv. The spawn plan is:
The binary is resolved to an absolute path via
resolveRuntimeBin('hermes') (a bare spawn('hermes') would ENOENT against the off-PATH user-site bin), and that binary’s directory is prepended to the child’s PATH so any tools Hermes itself shells out to are found.
Non-streaming lifecycle
Because Hermes has no token stream, the driver derivesRuntimeEvents from output lines and synthesizes the terminal result on process exit:
parseHermesLineparses each stdout line: a JSON frame → asession_idline → otherwise plain response text accumulated into the run summary.- The
session_idline is captured primarily from stderr inonClose(where quiet mode emits it, live-verified againsthermes-agent 0.15.2); the stdout parse is a forward-compatible tolerance in case a future CLI moves the line. - On process exit,
onClosesynthesizes oneresultevent. ASIGTERM/SIGKILLexit (a deliberate abort) is surfaced as a cleanabortedterminal, not a spurious error; a zero exit isok: true; a non-zero exit carries the stderr aserrorMessage.
session_id match simply means a fresh session next dispatch, never a failure.
Cost is estimated
The native→RuntimeEvent mapper (mapHermesEvent) emits a cost event with costUsd: null and estimated: true. Hermes records token usage, but its head-less USD is unreliable, so cost is reported as estimated (the same posture as Codex).
Session resume
The adapter’ssessionCodec serializes the captured native session id. On a same-runtime continuation the runner threads it back as ctx.resume, and the driver adds --resume <sessionId>, but only into a pre-existing home: a freshly created home cannot hold the prior session, so --resume is skipped for the home’s first run.
Provider precedence
Hermes resolves its model provider with a strict precedence (detectProvider + buildHermesSpawnPlan):
- The home
config.yaml’smodel.providerwins. When the seeded config names a provider (including a user-defined provider name), Clawboo passes no--providerflag; Hermes resolves it itself.detectProviderreads the provider with a block-scoped line scan of themodel:block (no YAML dependency), so the top-levelproviders:map can never false-positive. - Otherwise, a connected
OPENROUTER_API_KEYadds--provider openrouteras the no-config compatibility fallback; a pasted OpenRouter key just works. (An Anthropic or OpenAI key reused from the native connect derives--provider anthropic/--provider openai-apithe same way.) - Otherwise, a detected ChatGPT-subscription login adds
--provider openai-codex(with Hermes’s own codex default model) — the last rung beforeauto, AFTER every key rung, so keyed setups keep their exact behavior. - Otherwise, nothing is passed, and Hermes uses its built-in default (
auto).
codex_auth_missing failure.
Your own Hermes config is honored. There is no
--ignore-user-config. If you configure a provider in ~/.hermes/config.yaml, it is seed-copied into the home and that provider wins over the OpenRouter fallback.The persistent home and native-preservation invariants
When the runner resolves Hermes to a persistent home, the home lives at<clawboo home>/runtimes/hermes/<sanitized agentId> (under resolveClawbooDir(), so the CLAWBOO_HOME override applies). The agent id is collapsed to a safe path segment; dots are excluded entirely, so directory traversal is impossible by construction. The home is created owner-only (0700). Without a runner-materialized home (the conservative default), the driver falls back to a throwaway mkdtemp home, and nothing compounds.
Inside a Clawboo-owned home, Clawboo performs exactly three writes, and everything else belongs to Hermes:
These invariants define how Clawboo integrates with Hermes:
- Clawboo never writes into the user’s real
~/.hermes; it reads/copies from it only. (Theauth.jsonseed exists because a managed home outside~/.hermesis its OWN Hermes root — Hermes’s global-auth fallback never reaches the user’s real store from a spawned run.) .envis never seeded. Provider keys ride the spawned process’s environment (the vault is the source of truth); copying credentials would multiply secret surfaces on disk.- There is no Hermes-skills writer.
hermesSkills.tsexposes a read-only view (listNativeSkills) and a pure merge policy (mergeSkillSets) where the native skill wins on any name collision; Clawboo never shadows a self-created skill, and the absence of asyncSkillswriter is an explicit invariant, not an omission. --resumeonly into a pre-existing home, and never on a rotation successor; resuming an exhausted session would re-exhaust it; continuity rides the handoff note instead.
mcp.json in its home.
Verify it worked
- Re-fetch
GET /api/runtimes(or click Re-check in the card). Hermes’sconnectionStateshould readreadyandhealth.okshould betrue.healthresolves thehermesbinary viaresolveRuntimeBin(PATH plus user-install dirs), so a user-site install reads healthy. - After a run, the persistent home at
<clawboo home>/runtimes/hermes/<agentId>/should contain Hermes’s ownstate.db(growing across runs),MEMORY.md, andsessions/; the home compounding is the signal that preservation worked. The board task should move toin_review(non-empty diff) ordone, with a report-up summary comment.
Troubleshooting
A sandboxed
$HOME hides Hermes. When HOME is overridden (e.g. an e2e or dev sandbox), the Python user-site bin under the real home is hidden. For a sandboxed live run, set PYTHONUSERBASE to the real user base and prepend the Python bin dir to PATH. At your normal home, neither is needed.POST /api/runtimes/openclaw/... returns 404. OpenClaw is not a /api/runtimes runtime; only claude-code, codex, hermes, and clawboo-native are valid ids. OpenClaw connects through the Gateway. See OpenClaw.Related
- Connecting runtimes: install/connect/disconnect mechanics + the encrypted vault
- Runtimes overview: the full capability matrix
/api/runtimesreference: request/response shapes for install, connect, run- The board: the one board of record Hermes works against
- Worktrees and handoff: the isolated world a Hermes run executes in
- MCP servers: the Tasks/Memory/Tools/TeamChat spine Hermes attaches
- Scheduling: why Clawboo’s scheduler owns when-to-run, not Hermes’s cron
- Glossary: canonical term definitions