---
title: Hermes Provider
---

# Hermes Provider

## What it is

Omniscio can spawn Claude-Code-style sessions backed by the **Hermes CLI** (`hermes`, by **Nous Research**) as an alternative provider, alongside `claude` (the default), `codex`, `gemini`, `antigravity`, the Anthropic-compatible `deepseek` / `kimi`, `opencode`, and `cursor`. Hermes is a model-agnostic, tool-using coding agent you install locally; it runs on **its own** model + provider key (whatever you set with `hermes setup`), so a Hermes session is "a different agent on your own key," not Claude in a different harness.

**Why it exists:** a migration on-ramp. An existing Hermes user can run their own Hermes install from inside Omniscio — keeping the agent they know while moving onto Omniscio.

**Migrated to ACP (2026-08-01).** Hermes was previously driven as a per-turn one-shot spawn (`hermes -z`), which broke multi-turn memory in Hermes v0.19.0 (the `-z` mode stopped restoring conversation history). Hermes is now driven over the **Agent Client Protocol** — a kept-alive `hermes acp` child, the same pattern as Kimi Code and Gemini — so multi-turn memory is native and real. This was verified live: turn 1 "favorite color is teal" → "got it"; turn 2 "what color?" → "teal".

### What the user sees

A Hermes session looks almost identical in the sidebar and main pane to a Claude session — same status dots, same streaming bubbles, same Ctrl+Enter to send. The differences:

- **Amber "Hermes" pill** in the session header (next to the title), like Codex (emerald), Cursor (indigo), Gemini (violet). Hermes is a **first-class** provider — it shows a badge, is a per-project default, and is switch-to-able.
- **Token-by-token streaming.** Omniscio drives the kept-alive `hermes acp` child over JSON-RPC/NDJSON on stdio (the shared ACP layer), so the reply appears incrementally as it generates. When the turn completes, one final push replaces the bubble with the full accumulated text.
- **Real stateful multi-turn.** Because Hermes runs as a kept-alive ACP process, conversation memory is native — the agent remembers the full session history without Omniscio replaying prior turns as context.
- **Runs on YOUR Hermes config.** Omniscio passes Hermes nothing but your prompt — no model overrides, no `--model` arg — so the session uses whatever model and key you configured with `hermes setup`. Omniscio stores **no** key for Hermes.
- **No per-session dollar cost.** Hermes emits no token/cost over ACP v1, so Omniscio shows turn counts but the **Cost line is hidden / $0** for Hermes sessions. Your spend is tracked in your own provider account (the key you gave Hermes).
- **Images go straight to the agent when your Hermes build can read them.** On start-up Omniscio asks the running `hermes acp` child whether it accepts image prompts; if it does, an attached image is sent inline as an ACP image block. If it does not, the image is not sent and the session shows a short note saying so — describe the image in words, or switch to an engine with image support — never a silent drop. Documents are not delivered to Hermes (the compose area warns before you send).
- **Your project's MCP servers are handed to Hermes** when its session starts, through the same shared ACP layer Gemini and Kimi Code use; remote (HTTP) servers only when the child said it supports that transport. If Hermes refuses to start with the servers, Omniscio starts it once more without them and tells you in the session.

## Where to find it

### How to enable

> **Master toggle required first.** Hermes is an alternative provider — by default Omniscio ships as a Claude-only product, and the entire Hermes setup section is hidden in Settings → Accounts. Flip **Settings → Accounts → Show alternative AI providers** to ON (off by default) and the **Hermes (CLI provider)** panel appears. With the master off, the per-launch provider switcher hides Hermes. Your "Allow Hermes sessions" toggle is preserved across master-toggle flips.

Two gates must both pass (`getProviderSpawnReadiness` enforces them in this order) — **no API-key gate** (Hermes holds its own credential):

1. **Hermes CLI binary** — the `hermes` binary must be installed. Omniscio **detects** it (on PATH, the official Windows install dir `%LOCALAPPDATA%\hermes\venv\Scripts\hermes.exe`, or `$HERMES_HOME\venv\Scripts`) but does not install it. Install it from Nous Research's instructions, then run **`hermes setup`** to choose a model and add your provider key. Omniscio waits up to 30s and treats "present on disk = installed." If you just installed it, **restart Omniscio** so the running process picks up your updated PATH.
2. **Allow Hermes sessions** toggle — off by default. Same security stance as the other auto-running providers: enable only because you intend to use it. Lives in the Hermes panel as **Allow Hermes sessions in any project**.

Once both are green, you launch a Hermes session three ways:

- **Per-project default** — the Edit-Project "Default provider" radios and the "+ New Session" split-button include Hermes. New sessions in that project spawn Hermes automatically.
- **Per-launch override** — on a fresh (zero-message) session, the **Change-provider button** (the launch-config pickers on a fresh session) lets you pick Hermes before sending the first message. A not-ready provider appears disabled with an "Install…" suffix that deep-links into the relevant Settings panel.
- **Programmatically** — anything that creates a session with `provider: 'hermes'` (recipes, the CLI control server, deep-links). The same two readiness gates apply on the backend.

## How it behaves

### No virtual project

Hermes has **no dedicated `__hermes__` sidebar entry** (unlike `__codex__` / `__gemini__` / `__antigravity__`), and therefore no sentinel bypass of the "Allow Hermes sessions" toggle — the toggle always applies. You spawn Hermes inside any real project. (This matches Cursor/OpenCode; only Codex/Gemini/Anti-Gravity have virtual projects.)

### Error states and fixes

Two gap codes (no key gap — Hermes uses its own credential):

| Gap              | What it means                              | Click-to-fix lands you at…                         |
| ---------------- | ------------------------------------------ | -------------------------------------------------- |
| `toggle-off`     | "Allow Hermes sessions" is OFF in Settings | Settings → Accounts → Allow Hermes sessions toggle |
| `binary-missing` | `hermes` CLI not found                     | Settings → Accounts → Hermes CLI panel             |

If a turn fails after Hermes is found, the most common cause is Hermes not being fully set up — the failure message points you at `hermes setup` (pick a model + add a key). An auth failure on session creation surfaces "run `hermes setup` to pick a model and add a key, then try again."

If a turn is rejected because Hermes's own working memory outgrew the model's window (usually a command that printed a huge amount of output), the message says it ran out of room and that the conversation is saved. Omniscio has already replaced the overflowed `hermes acp` process, so your next message starts fresh on a compact copy of the conversation — usually no new session is needed. If it still runs out of room, the conversation may be too long for the model, so start a new session.

## For agents

### How it works under the hood

**Kept-alive ACP child per session.** Omniscio spawns `hermes acp --accept-hooks` once and keeps it alive for the session's lifetime — the same Agent Client Protocol pattern as Gemini and Kimi Code. JSON-RPC 2.0 frames flow over the child's stdio (NDJSON framing, shared `AcpClient` transport and `acp-update-translator` layer). The `hermes acp` child starts **lazily** on the first message, so creating or switching to a Hermes session is instant. On an app restart the manager re-registers and re-spawns a fresh child on the next message.

**`hermes acp --accept-hooks`.** The `acp` subcommand runs Hermes in its ACP server mode. `--accept-hooks` auto-approves headless shell hooks (no TTY in the child process) so turns don't block waiting for interactive confirmation. No `--model` arg is passed — Hermes runs the user's configured model from `hermes setup` / config.yaml; AMC must not override it.

**Real stateful multi-turn.** Because the ACP session is kept alive, Hermes maintains the full conversation history natively. There is no Omniscio-side history replay on every turn (the old `-z` mode required this workaround, and it broke in v0.19.0). This migration was the fix.

**Auth stays in Hermes's own config.yaml.** Omniscio spawns with a clean environment (`buildCleanSpawnEnv()`) — no key is injected, no ambient credential can silently hijack billing. A `session/new` auth failure is humanized to "run `hermes setup`"; Omniscio does NOT attempt an ACP `authenticate` call (Hermes's auth methodIds are undocumented in v1).

**Permission requests — v1 auto-approve.** The permission handler auto-approves all tool calls (`allow`). This is the same documented v1 posture as Kimi Code: acceptable for v1 because we can't yet confirm whether Hermes's ACP server even sends `session/request_permission` vs. auto-running tools itself. Adding the shared dangerous-path denylist net (the same one Gemini's handler uses) is a tracked hardening follow-up.

**Cost tracking ($0 today).** Hermes ACP v1 exposes no token/cost data, so each turn records `$0` via `updateSessionCost` (`costReporting: 'none'`). `numTurns` is bumped per turn so the sidebar's blank-session filter keeps the session visible. Your real spend lives in your own provider dashboard.

**Interrupt.** Pressing stop sends an ACP `session/cancel` notification (advisory) and authoritatively concludes the turn in the manager — the same pattern as Kimi Code. Partial streamed text is preserved and status returns to `ready`.

**Routing.** A `SESSION_LAUNCH` (and subsequent send / interrupt / terminate / change-provider) with `provider: 'hermes'` is dispatched to `hermesAcpSessionManager` through the dispatch maps derived from its ONE entry in `engine-registrations.ts` — the same entry supplies its send route, the shared ACP strategy. The session row stores `provider: 'hermes'` in its DB column; the `sessions.provider` validation triggers are refreshed from the provider registry at boot, so the id is accepted without a dedicated migration (the 2026-06 migration that first admitted it remains as history). `restartResumable: false` excludes Hermes from the crash/restart-resume nets (the ACP session state lives in the child, not in a persisted external id the next child can resume from) — the wiring grid's crash-restart cell reads `missing` for it, honestly.

**Capabilities.** `hermes` is declared with `{ images: true, mcp: true, ssh: false, permissionPrompts: false, multiTurn: true }`. `images: true` and `mcp: true` are ceilings, not promises: the shared slim ACP base reads what the running `hermes acp` child advertised on its `initialize` handshake and delivers only that — inline ACP image blocks when it advertised image prompts (otherwise the image is withheld and a visible note is posted in the session), and the project's resolved MCP servers on `session/new` through the shared in-band connector (remote HTTP servers only when it advertised that transport; if the child refuses to start with the servers it is started once more without them, and the session says so). No SSH remote, no permission-prompt interception (auto-approve), and `multiTurn: true` is genuinely true (native ACP session memory — the `-z` regression that made this a lie is fixed).

### Verified live against `hermes acp` (2026-08-01)

Multi-turn memory was confirmed end-to-end with the ACP transport: turn 1 "favorite color is teal" → agent responded "got it"; turn 2 "what color?" → agent replied "teal". This directly validates the fix: under the old `-z` one-shot transport, turn 2 would have produced no memory of turn 1.

### Files

- [src/main/services/engines/hermes-acp-session-manager.ts](../../src/main/services/engines/hermes-acp-session-manager.ts) — a ~20-line config subclass of the shared `SlimAcpSessionManager` base ([slim-acp-session-manager.ts](../../src/main/services/engines/slim-acp-session-manager.ts) — F016, shared with Kimi Code; itself over `BaseExternalSessionManager`); supplies the `HermesAcpClient` factory + handshake predicate + config strings (singleton `hermesAcpSessionManager`)
- [src/main/process/hermes-acp-client.ts](../../src/main/process/hermes-acp-client.ts) — spawns `hermes acp --accept-hooks` and drives `session/new` / `session/prompt` / `session/cancel` / graceful stop over the shared `SlimAcpSessionClient`, which performs the one ACP `initialize` handshake and keeps what Hermes advertised ([acp-capabilities.ts](../../src/main/process/acp-capabilities.ts) reads it); reuses the provider-agnostic `AcpClient` transport and `acp-update-translator`
- [src/main/services/hermes-binary-resolver.ts](../../src/main/services/hermes-binary-resolver.ts) — locate `hermes` (30s slow-probe + resolve-by-existence + the `%LOCALAPPDATA%\hermes\venv\Scripts` / `$HERMES_HOME` candidates)
- [src/main/services/provider-setup/provider-readiness.ts](../../src/main/services/provider-setup/provider-readiness.ts) + [src/main/services/providers/main-registry.ts](../../src/main/services/providers/main-registry.ts) — the 2-gate readiness (toggle + binary, no key)
- [src/shared/providers/registry.ts](../../src/shared/providers/registry.ts) — the `hermes` descriptor (`runtimeKind: 'persistent-external'`, `pickerOrder: 7`, `restartResumable: false`, `costReporting: 'none'`); the single source the picker, badge, and Zod enums all derive from
- [src/main/services/providers/engine-registrations.ts](../../src/main/services/providers/engine-registrations.ts) — Hermes's ONE registration (manager + the shared ACP send strategy); [session-manager-registry.ts](../../src/main/services/providers/session-manager-registry.ts) reads the dispatch maps derived from it
- [src/main/db/sessions-provider-allow-list.ts](../../src/main/db/sessions-provider-allow-list.ts) — the boot-time refresh that keeps the `sessions.provider` triggers equal to the registry (the dated 2026-06 migration that first admitted `'hermes'` is history, not a step)
- [src/renderer/src/components/ui/HermesIcon.tsx](../../src/renderer/src/components/ui/HermesIcon.tsx) + [src/renderer/src/features/sessions/ProviderBadge.tsx](../../src/renderer/src/features/sessions/ProviderBadge.tsx) (amber pill) + [src/renderer/src/features/sessions/ChangeProviderButton.tsx](../../src/renderer/src/features/sessions/ChangeProviderButton.tsx) — icon + per-launch switcher
- [src/renderer/src/features/settings/sections/accounts/AccountSettings.tsx](../../src/renderer/src/features/settings/sections/accounts/AccountSettings.tsx) — the Hermes (CLI provider) Settings section: allow-toggle + the install/setup card (no key field)
- [.claude/memory/contracts/provider-registry-contract.md](../../.claude/memory/contracts/provider-registry-contract.md) — the feature contract (8 invariants + safe-change checklist)
- [tests/unit/process/hermes-acp-client.test.ts](../../tests/unit/process/hermes-acp-client.test.ts) — locks the ACP client: spawn args, handshake, auth-error detection, permission option selection, graceful stop
- [tests/unit/services/hermes-acp-session-manager.test.ts](../../tests/unit/services/hermes-acp-session-manager.test.ts) — locks the session manager: lazy spawn, turn dispatch, stall watchdog, interrupt

### Comparison with other providers

| Concern             | Hermes                                                      | Kimi Code                                | Gemini                                    |
| ------------------- | ----------------------------------------------------------- | ---------------------------------------- | ----------------------------------------- |
| Auth                | **None in Omniscio** (Hermes's own config.yaml key)         | Kimi Code CLI login (`~/.kimi-code/`)    | Google API key (`GEMINI_API_KEY`)         |
| Underlying model    | Your choice (`hermes setup`)                                | Kimi Code default model                  | Gemini                                    |
| Process model       | Persistent `hermes acp` child (ACP)                         | Persistent `kimi acp` child (ACP)        | Persistent `gemini --acp` child (ACP)     |
| Streaming           | Token-by-token (ACP `session/update` JSON-RPC)              | Token-by-token (ACP JSON-RPC)            | Token-by-token (ACP JSON-RPC)             |
| Multi-turn memory   | **Native** (ACP session — real, verified 2026-08-01)        | **Native** (ACP session)                 | **Native** (ACP session)                  |
| Cost emitted        | **No** (records $0)                                         | **No** (records $0)                      | **No** (Google-billed)                    |
| Images              | Inline ACP blocks when the child advertises them, else a note | Same (shared slim ACP base)            | Inline ACP blocks                         |
| MCP servers         | Project servers on `session/new` when advertised            | Same (shared slim ACP base)              | Yes (in-band connector)                   |
| Readiness gates     | **2** (toggle + binary, no key)                             | **2** (toggle + binary, no key)          | 3 (toggle + binary + key)                 |
| Per-project default | **Yes**                                                     | Yes                                      | Yes                                       |
| Session-header pill | **Amber "Hermes"**                                          | Kimi Code mark                           | Gemini bloom                              |
| `restartResumable`  | `false` (ACP session state lives in the child)              | `false`                                  | N/A (has its own `--resume <uuid>`)       |

## Related

- [kimi-code-provider.md](kimi-code-provider.md) — its slim-ACP twin: same `SlimAcpSessionManager` base (`hermes acp` vs `kimi acp`), same 2-gate no-key readiness, same handshake-gated images + MCP servers, same auto-approve v1 posture. (Not [kimi-provider.md](kimi-provider.md) — that is the Kimi MODEL run through the Claude CLI.)
- [ai-providers.md](ai-providers.md) — the cross-provider overview
