ABS guide source notes

Source Notes: Hermes Agent Setup Guide

Official-doc excerpts, local verification notes, and source material used to draft the Hermes Agent setup guide.

Review copy · Updated July 10, 2026

Hermes Agent source notes for ABS guide test

Sources: official Hermes docs llms-full.txt, local Hermes CLI, GitHub README.

Installation

Installation

Get Hermes Agent up and running in under two minutes!

:::tip Platform Support For the full platform support matrix (which OSes, distribution methods, and platform-gated features are supported), see Platform Support. :::

Quick Install

To easily install the command-line and desktop applications, download the Hermes Desktop installer from our website and run it.

Without Hermes Desktop:

For a command-line only install without Hermes Desktop, run:

Linux / macOS / WSL2 / Android (Termux)

curl -fsSL https://hermes-agent.nousresearch.com/install.sh | bash

Windows (native)

Run in powershell:

iex (irm https://hermes-agent.nousresearch.com/install.ps1) 

If you want to install & run Hermes Desktop after a command-line only install, simply run

hermes desktop

What the Installer Does

The installer handles everything automatically — all dependencies (Python, Node.js, ripgrep, ffmpeg), the repo clone, virtual environment, global hermes command setup, and LLM provider configuration. By the end, you’re ready to chat.

Install Layout

Where the installer puts things depends on whether you’re installing as a normal user or as root:

InstallerCode lives athermes binaryData directory
Per-user (git installer)~/.hermes/hermes-agent/~/.local/bin/hermes (symlink)~/.hermes/
Root-mode (sudo curl … | sudo bash)/usr/local/lib/hermes-agent//usr/local/bin/hermes/root/.hermes/ (or $HERMES_HOME)

The root-mode FHS layout (/usr/local/lib/…, /usr/local/bin/hermes) matches where other system-wide developer tools land on Linux. It’s useful for shared-machine deployments where one system install should serve every user. Per-user config (auth, skills, sessions) still lives under each user’s ~/.hermes/ or explicit HERMES_HOME.

After Installation

Reload your shell and start chatting:

source ~/.bashrc   # or: source ~/.zshrc
hermes             # Start chatting!

To reconfigure individual settings later, use the dedicated commands:

hermes model          # Choose your LLM provider and model
hermes tools          # Configure which tools are enabled
hermes gateway setup  # Set up messaging platforms
hermes config set     # Set individual config values
hermes setup          # Or run the full setup wizard to configure everything at once

:::tip Fastest path: Nous Portal One subscription covers 300+ models plus the Tool Gateway (web search, image generation, TTS, cloud browser). Skip the per-tool key juggling:

hermes setup --portal

That logs you in, sets Nous as your provider, and turns on the Tool Gateway in one command. :::

Quickstart

Quickstart

This guide gets you from zero to a working Hermes setup that survives real use. Install, choose a provider, verify a working chat, and know exactly what to do when something breaks.

Prefer to watch?

Onchain AI Garage put together a Masterclass walkthrough of installation, setup, and basic commands — a good companion to this page if you’d rather follow along on video. For more, see the full Hermes Agent Tutorials & Use Cases playlist.

Who this is for

  • Brand new and want the shortest path to a working setup
  • Switching providers and don’t want to lose time to config mistakes
  • Setting up Hermes for a team, bot, or always-on workflow
  • Tired of “it installed, but it still does nothing”

The fastest path

Pick the row that matches your goal:

GoalDo this firstThen do this
I just want Hermes working on my machinehermes setupRun a real chat and verify it responds
I already know my providerhermes modelSave the config, then start chatting
I want a bot or always-on setuphermes gateway setup after CLI worksConnect Telegram, Discord, Slack, or another platform
I want a local or self-hosted modelhermes model → custom endpointVerify the endpoint, model name, and context length
I want multi-provider fallbackhermes model firstAdd routing and fallback only after the base chat works

Rule of thumb: if Hermes cannot complete a normal chat, do not add more features yet. Get one clean conversation working first, then layer on gateway, cron, skills, voice, or routing.

CLI Interface

CLI Interface

Hermes Agent’s CLI is a full terminal user interface (TUI) — not a web UI. It features multiline editing, slash-command autocomplete, conversation history, interrupt-and-redirect, and streaming tool output. Built for people who live in the terminal.

:::tip First-time setup One command — hermes setup --portal — and you’re ready to hermes chat. See Nous Portal. :::

:::tip Hermes also ships a modern TUI with modal overlays, mouse selection, and non-blocking input. Launch it with hermes --tui — see the TUI guide. :::

Running the CLI

# Start an interactive session (default)
hermes

# Single query mode (non-interactive)
hermes chat -q "Hello"

# With a specific model
hermes chat --model "anthropic/claude-sonnet-4"

# With a specific provider
hermes chat --provider nous        # Use Nous Portal
hermes chat --provider openrouter  # Force OpenRouter

# With specific toolsets
hermes chat --toolsets "web,terminal,skills"

# Start with one or more skills preloaded
hermes -s hermes-agent-dev,github-auth
hermes chat -s github-pr-workflow -q "open a draft PR"

# Resume previous sessions
hermes --continue             # Resume the most recent CLI session (-c)
hermes --resume <session_id>  # Resume a specific session by ID (-r)

# Verbose mode (debug output)
hermes chat --verbose

# Isolated git worktree (for running multiple agents in parallel)
hermes -w                         # Interactive mode in worktree
hermes -w -z "Fix issue #123"     # Single query in worktree

Interface Layout

Stylized preview of the Hermes CLI layout showing the banner, conversation area, and fixed input prompt.

The Hermes CLI banner, conversation stream, and fixed input prompt rendered as a stable docs figure instead of fragile text art.

The welcome banner shows your model, terminal backend, working directory, available tools, and installed skills at a glance.

Status Bar

A persistent status bar sits above the input area, updating in real time:

 ⚕ claude-sonnet-4-20250514 │ 12.4K/200K │ [██████░░░░] 6% │ $0.06 │ 15m
ElementDescription
Model nameCurrent model (truncated if longer than 26 chars)
Token countContext tokens used / max context window
Context barVisual fill indicator with color-coded thresholds
CostEstimated session cost (or n/a for unknown/zero-priced models)
🗜️ NContext compression count — how many times the running session has been auto-compressed. Appears once the first compression fires.
▶ NActive background tasks — how many /background prompts are still running in the current session. Appears whenever at least one task is in flight.
DurationElapsed session time
⚠ YOLOYOLO mode warning — shown whenever HERMES_YOLO_MODE is on (either hermes --yolo at launch or /yolo toggled mid-session). Mirrors the banner-line warning so you can’t forget you’re in auto-approve mode.

The bar adapts to terminal width — full layout at ≥ 76 columns, compact at 52–75, minimal (model + duration, plus the YOLO badge when active) below 52.

Context color coding:

ColorThresholdMeaning
Green< 50%Plenty of room
Yellow50–80%Getting full
Orange80–95%Approaching limit
Red≥ 95%Near overflow — consider /compress

Use /usage for a detailed breakdown including per-category costs (input vs output tokens).

Session Resume Display

When resuming a previous session (hermes -c or hermes --resume <id>), a “Previous Conversation” panel appears between the banner and the input prompt, showing a compact recap of the conversation history. See Sessions — Conversation Recap on Resume for details and configuration.

Keybindings

KeyAction
EnterSend message
Alt+Enter, Ctrl+J, or Shift+EnterNew line (multi-line input). Shift+Enter requires a terminal that distinguishes it from Enter — see below. On Windows Terminal, Alt+Enter is captured by the terminal (fullscreen toggle); use Ctrl+Enter or Ctrl+J instead.
Alt+VPaste an image from the clipboard when supported by the terminal
Ctrl+VPaste text and opportunistically attach clipboard images
Ctrl+BStart/stop voice recording when voice mode is enabled (voice.record_key, default: ctrl+b)
Ctrl+GOpen the current input buffer in $EDITOR (vim/nvim/nano/VS Code/etc.). Save and quit to send the edited text as the next prompt — ideal for long, multi-paragraph prompts.
Ctrl+X Ctrl+EEmacs-style alternate binding for the external editor (same behavior as Ctrl+G).
Ctrl+CInterrupt agent (double-press within 2s to force exit)
Ctrl+DExit
Ctrl+ZSuspend Hermes to background (Unix only). Run fg in the shell to resume.
TabAccept auto-suggestion (ghost text) or autocomplete slash commands

Multiline paste preview. When you paste a multi-line block, the CLI echoes a compact single-line preview ([pasted: 47 lines, 1,842 chars — press Enter to send]) instead of dumping the whole payload into the scrollback. The full content is still what gets sent; this is just display polish.

Markdown stripping in final responses. The CLI strips the most verbose markdown fences and **bold** / *italic* wrappers from final agent replies so they render as readable terminal prose rather than raw source. Code blocks and lists are preserved. This does not affect gateway platforms or tool results — they keep their markdown for native rendering.

Slash Commands

Type / to see the autocomplete dropdown. Hermes supports a large set of CLI slash commands, dynamic skill commands, and user-defined quick commands.

Common examples:

CommandDescription
/helpShow command help
/modelShow or change the current model
/toolsList currently available tools
/skills browseBrowse the skills hub and official optional skills
/background <prompt>Run a prompt in a separate background session
/skinShow or switch the active CLI skin
/voice onEnable CLI voice mode (press Ctrl+B to record)
/voice ttsToggle spoken playback for Hermes replies
/reasoning highIncrease reasoning effort
/title My SessionName the current session
/statusShow session info — model/profile/tokens/duration — followed by a local Session recap block (recent turn counts, top tools used, files touched, latest user prompt + assistant reply). Pure local compute; no LLM call.
/sessionsOpen an interactive session picker right inside the classic CLI (same surface the TUI uses). Type to filter, arrow keys to

Configuration

Declarative Settings

The settings option accepts an arbitrary attrset that is rendered as config.yaml. It supports deep merging across multiple module definitions (via lib.recursiveUpdate), so you can split config across files:

# base.nix
services.hermes-agent.settings = {
  model.default = "anthropic/claude-sonnet-4";
  toolsets = [ "all" ];
  terminal = { backend = "local"; timeout = 180; };
};

# personality.nix
services.hermes-agent.settings = {
  display = { compact = false; personality = "kawaii"; };
  memory = { memory_enabled = true; user_profile_enabled = true; };
};

Both are deep-merged at evaluation time. Nix-declared keys always win over keys in an existing config.yaml on disk, but user-added keys that Nix doesn’t touch are preserved. This means if the agent or a manual edit adds keys like skills.disabled or streaming.enabled, they survive nixos-rebuild switch.

:::note Model naming settings.model.default uses the model identifier your provider expects. With OpenRouter (the default), these look like "anthropic/claude-sonnet-4" or "google/gemini-3-flash". If you’re using a provider directly (Anthropic, OpenAI), set settings.model.base_url to point at their API and use their native model IDs (e.g., "claude-sonnet-4-20250514"). When no base_url is set, Hermes defaults to OpenRouter. :::

:::tip Discovering available config keys Run nix build .#configKeys && cat result to see every leaf config key extracted from Python’s DEFAULT_CONFIG. You can paste your existing config.yaml into the settings attrset — the structure maps 1:1. :::

Full example: all commonly customized settings
{ config, ... }: {
  services.hermes-agent = {
    enable = true;
    container.enable = true;

    # ── Model ──────────────────────────────────────────────────────────
    settings = {
      model = {
        base_url = "https://openrouter.ai/api/v1";
        default = "anthropic/claude-opus-4.6";
      };
      toolsets = [ "all" ];
      max_turns = 100;
      terminal = { backend = "local"; cwd = "."; timeout = 180; };
      compression = {
        enabled = true;
        threshold = 0.85;
        summary_model = "google/gemini-3-flash-preview";
      };
      memory = { memory_enabled = true; user_profile_enabled = true; };
      display = { compact = false; personality = "kawaii"; };
      agent = { max_turns = 60; verbose = false; };
    };

    # ── Secrets ────────────────────────────────────────────────────────
    environmentFiles = [ config.sops.secrets."hermes-env".path ];

    # ── Documents ──────────────────────────────────────────────────────
    documents = {
      "USER.md" = ./documents/USER.md;
    };

    # ── MCP Servers ────────────────────────────────────────────────────
    mcpServers.filesystem = {
      command = "npx";
      args = [ "-y" "@modelcontextprotocol/server-filesystem" "/data/workspace" ];
    };

    # ── Container options ──────────────────────────────────────────────
    container = {
      image = "ubuntu:24.04";
      backend = "docker";
      hostUsers = [ "your-username" ];
      extraVolumes = [ "/home/user/projects:/projects:rw" ];
      extraOptions = [ "--gpus" "all" ];
    };

    # ── Service tuning ─────────────────────────────────────────────────
    addToSystemPackages = true;
    extraArgs = [ "--verbose" ];
    restart = "always";
    restartSec = 5;
  };
}

Escape Hatch: Bring Your Own Config

If you’d rather manage config.yaml entirely outside Nix, use configFile:

services.hermes-agent.configFile = /etc/hermes/config.yaml;

This bypasses settings entirely — no merge, no generation. The file is copied as-is to $HERMES_HOME/config.yaml on each activation.

Customization Cheatsheet

Quick reference for the most common things Nix users want to customize:

I want to…OptionExample
Change the LLM modelsettings.model.default"anthropic/claude-sonnet-4"
Use a different provider endpointsettings.model.base_url"https://openrouter.ai/api/v1"
Add API keysenvironmentFiles[ config.sops.secrets."hermes-env".path ]
Give the agent a personality${services.hermes-agent.stateDir}/.hermes/SOUL.mdmanage the file directly
Add MCP tool serversmcpServers.<name>See MCP Servers
Enable Discord/Telegram/SlackextraDependencyGroups[ "messaging" ]
Mount host directories into containercontainer.extraVolumes[ "/data:/data:rw" ]
Pass GPU access to containercontainer.extraOptions[ "--gpus" "all" ]
Use Podman instead of Dockercontainer.backend"podman"
Share state between host CLI and containercontainer.hostUsers[ "sidbin" ]
Make extra tools available to the agentextraPackages[ pkgs.pandoc pkgs.imagemagick ]
Use a custom base imagecontainer.image"ubuntu:24.04"
Override the hermes packagepackageinputs.hermes-agent.packages.${system}.default.override { ... }
Change state directorystateDir"/opt/hermes"
Set the agent’s working directoryworkingDirectory"/home/user/projects"

Tools & Toolsets

Tools & Toolsets

Tools are functions that extend the agent’s capabilities. They’re organized into logical toolsets that can be enabled or disabled per platform.

Available Tools

Hermes ships with a broad built-in tool registry covering web search, browser automation, terminal execution, file editing, memory, delegation, scheduled tasks, Home Assistant, and more.

:::note Honcho cross-session memory is available as a memory provider plugin (plugins/memory/honcho/), not as a built-in toolset. See Plugins for installation. :::

High-level categories:

CategoryExamplesDescription
Webweb_search, web_extractSearch the web and extract page content.
X Searchx_searchSearch X (Twitter) posts and threads via xAI’s built-in x_search Responses tool — gated on xAI credentials (SuperGrok OAuth or XAI_API_KEY); off by default, opt in via hermes tools → 🐦 X (Twitter) Search.
Terminal & Filesterminal, process, read_file, patchExecute commands and manipulate files.
Browserbrowser_navigate, browser_snapshot, browser_visionInteractive browser automation with text and vision support.
Mediavision_analyze, image_generate, text_to_speechMultimodal analysis and generation.
Agent orchestrationtodo, clarify, execute_code, delegate_taskPlanning, clarification, code execution, and subagent delegation.
Memory & recallmemory, session_searchPersistent memory and session search.
AutomationcronjobScheduled tasks with create/list/update/pause/resume/run/remove actions. Outbound delivery is handled by cron’s own delivery, the hermes send CLI, and the gateway notifier — not by an agent-callable tool.
Integrationsha_*, MCP server toolsHome Assistant, MCP, and other integrations.

For the authoritative code-derived registry, see Built-in Tools Reference and Toolsets Reference.

:::tip Nous Tool Gateway Paid Nous Portal subscribers can use web search, image generation, TTS, and browser automation through the Tool Gateway — no separate API keys needed. Run hermes model to enable it, or configure individual tools with hermes tools. :::

Using Toolsets

# Use specific toolsets
hermes chat --toolsets "web,terminal"

# See all available tools
hermes tools

# Configure tools per platform (interactive)
hermes tools

Common toolsets include web, search, terminal, file, browser, vision, image_gen, skills, tts, todo, memory, session_search, cronjob, code_execution, delegation, clarify, homeassistant, messaging, spotify, discord, discord_admin, debugging, and safe.

See Toolsets Reference for the full set, including platform presets such as hermes-cli, hermes-telegram, and dynamic MCP toolsets like mcp-<server>.

Terminal Backends

The terminal tool can execute commands in different environments:

BackendDescriptionUse Case
localRun on your machine (default)Development, trusted tasks
dockerIsolated containersSecurity, reproducibility
sshRemote serverSandboxing, keep agent away from its own code
singularityHPC containersCluster computing, rootless
modalCloud executionServerless, scale
daytonaCloud sandbox workspacePersistent remote dev environments

Configuration

# In ~/.hermes/config.yaml
terminal:
  backend: local    # or: docker, ssh, singularity, modal, daytona
  cwd: "."          # Working directory
  timeout: 180      # Command timeout in seconds

Docker Backend

terminal:
  backend: docker
  docker_image: python:3.11-slim

One persistent container, shared across the whole process. Hermes starts a single long-lived container on first use (docker run -d ... sleep 2h) and routes every terminal, file, and execute_code call through docker exec into that same container. Working-directory changes, installed packages, environment tweaks, and files written to /workspace all carry over from one tool call to the next, across /new, /reset, and delegate_task subagents, for the lifetime of the Hermes process. The container is stopped and removed on shutdown.

This means the Docker backend behaves like a persistent sandbox VM, not a fresh container per command. If you pip install foo once, it’s there for the rest of the session. If you cd /workspace/project, subsequent ls calls see that directory. See Configuration → Docker Backend for the full lifecycle details and the container_persistent flag that controls whether /workspace and /root survive across Hermes restarts.

SSH Backend

Recommended for security — agent can’t modify its own code:

terminal:
  backend: ssh
# Set credentials in ~/.hermes/.env
TERMINAL_SSH_HOST=my-server.example.com
TERMINAL_SSH_USER=myuser
TERMINAL_SSH_KEY=~/.ssh/id_rsa

Singularity/Apptainer

# Pre-build SIF for parallel workers
apptainer build ~/python.sif docker://python:3.11-slim

# Configure
hermes config set terminal.backend singularity
hermes config set terminal.singularity_image ~/python.sif
uv pip install modal
modal setup
hermes config set terminal.backend modal

Container Resources

Configure CPU, memory, disk, and persistence for all container backends:

terminal:
  backend: docker  # or singularity, modal, daytona
  container_cpu: 1              # CPU cores (default: 1)
  container_memory: 5120        # Memory in MB (default: 5GB)
  container_disk: 51200         # Disk in MB (default: 50GB)
  container_persistent: true    # Persist filesystem across sessions (default: true)

When container_persistent: true, installed packages, files, and config survive across sessions.

Container Security

All container backends run with security hardening:

  • Read-only root filesystem (Docker)
  • All Linux capabilities dropped
  • No privilege escalation
  • PID limits (256 processes)
  • Full namespace isolation
  • Persistent workspace via volumes, not writable root layer

Docker can optionally receive an explicit env allowlist via terminal.docker_forward_env, but forwarded variables are visible to commands inside the container and should be treated as exposed to that session.

Background Process Management

Start background processes and manage them:

terminal(command="pytest -v tests/", background=true)
# Returns: {"session_id": "proc_abc123", "pid": 12345}

# Then manage with the process tool:
process(action="list")       # Show all running processes
process(action="poll", se


<!-- EXTRACT: # Skills System -->
# Skills System

# Skills System

Skills are on-demand knowledge documents the agent can load when needed. They follow a **progressive disclosure** pattern to minimize token usage and are compatible with the [agentskills.io](https://agentskills.io/specification) open standard.

All skills live in **`~/.hermes/skills/`** — the primary directory and source of truth. On fresh install, bundled skills are copied from the repo. Hub-installed and agent-created skills also go here. The agent can modify or delete any skill.

You can also point Hermes at **external skill directories** — additional folders scanned alongside the local one. See [External Skill Directories](#external-skill-directories) below.

See also:

- [Bundled Skills Catalog](/reference/skills-catalog)
- [Official Optional Skills Catalog](/reference/optional-skills-catalog)

## Starting with a blank slate

By default every profile is seeded with the bundled skill catalog, and each `hermes update` adds any newly bundled skills. If you want a profile with **no bundled skills**and that stays empty across updates — you have two paths:

**At install time** (applies to the default `~/.hermes` profile):

```bash
curl -fsSL https://hermes-agent.nousresearch.com/install.sh | bash -s -- --no-skills

At profile-create time (named profiles):

hermes profile create research --no-skills

On an already-installed profile (default or named), toggle it at runtime:

hermes skills opt-out            # stop future seeding — nothing on disk is touched
hermes skills opt-out --remove   # also delete UNMODIFIED bundled skills (confirms first)
hermes skills opt-in --sync      # undo: remove the marker and re-seed now

All three paths write a .no-bundled-skills marker into the profile directory. While the marker is present, the installer, hermes update, and any skill sync all skip bundled-skill seeding for that profile. Delete the marker (or run hermes skills opt-in) to re-enable.

:::note Safe by default hermes skills opt-out only stops future seeding — it never deletes anything already on disk. The optional --remove flag deletes bundled skills only when they are unmodified (byte-identical to the version Hermes installed). Skills you have edited, skills installed from the hub, and skills you wrote yourself are always kept. :::

Using Skills

Every installed skill is automatically available as a slash command:

# In the CLI or any messaging platform:
/gif-search funny cats
/axolotl help me fine-tune Llama 3 on my dataset
/github-pr-workflow create a PR for the auth refactor
/plan design a rollout for migrating our auth provider

# Just the skill name loads it and lets the agent ask what you need:
/excalidraw

Stacking multiple skills in one command

You can invoke several skills in a single message by chaining slash commands at the start — every leading /skill token (up to 5) is loaded, and the rest becomes your instruction:

/github-pr-workflow /test-driven-development fix issue #123 and open a PR

Parsing stops at the first token that isn’t an installed skill, so arguments that happen to start with / (like file paths) are never swallowed:

/ocr-and-documents /tmp/scan.pdf extract the tables   # loads one skill; /tmp/scan.pdf is the argument

For combinations you use repeatedly, prefer a skill bundle — same effect under one short command.

The bundled plan skill is a good example. Running /plan [request] loads the skill’s instructions, telling Hermes to inspect context if needed, write a markdown implementation plan instead of executing the task, and save the result under .hermes/plans/ relative to the active workspace/backend working directory.

You can also interact with skills through natural conversation:

hermes chat --toolsets skills -q "What skills do you have?"
hermes chat --toolsets skills -q "Show me the axolotl skill"

Learning a skill from sources (/learn)

/learn is the fast way to turn something you already know — or a pile of reference material — into a reusable skill, without hand-writing the SKILL.md. It is open-ended: point it at anything you can describe and the agent gathers the material with the tools it already has, then authors a skill that follows the house authoring standards (≤60-char description, the standard section order, Hermes-tool framing, no invented commands).

# A local SDK or doc directory — read with read_file / search_files
/learn the REST client in ~/projects/acme-sdk, focus on auth + pagination

# An online doc page — fetched with web_extract
/learn https://docs.example.com/api/quickstart

# The workflow you just walked the agent through in this conversation
/learn how I just deployed the staging server

# Pasted notes / a described procedure
/learn filing an expense: open the portal, New > Expense, attach the receipt, submit

Because the live agent does the sourcing, /learn works the same in the CLI, the messaging gateway, the TUI, and the dashboard — and on any terminal backend (local, Docker, remote), since there is no separate ingestion engine. In the dashboard, the Skills page has a Learn a skill button that opens a panel with a directory field, a URL field, and an open-ended text box; it composes a /learn request and runs it in chat.

There is no model-tool footprint: /learn builds a standards-guided prompt and hands it to the agent as a normal turn. The agent saves the result with the skill_manage tool, so the write-approval gate applies if you have it on.

Progressive Disclosure

Skills use a token-efficient loading pattern:

Level 0: skills_list()           → [{name, description, category}, ...]   (~3k tokens)
Level 1: skill_view(name)        → Full content + metadata       (varies)
Level 2: skill_view(name, path)  → Specific reference file       (varies)

The agent only loads the full skill content when it actually needs it.

SKILL.md Format



<!-- EXTRACT: # Memory -->
# Memory Configuration

```yaml
memory:
  memory_enabled: true
  user_profile_enabled: true
  memory_char_limit: 2200   # ~800 tokens
  user_char_limit: 1375     # ~500 tokens
  write_approval: false     # true = require approval before any memory write

With memory.write_approval: true, memory writes need your approval before they land: interactive CLI turns prompt inline; messaging sessions and the background self-improvement review stage the write for /memory pending/memory approve <id> / /memory reject <id> review. Toggle at runtime with /memory approval on|off. See Controlling memory writes.

Context File Truncation

Controls how much content Hermes loads from each automatic context file before applying head/tail truncation. This applies to files injected into the system prompt such as SOUL.md, .hermes.md, AGENTS.md, CLAUDE.md, and .cursorrules. It does not affect the read_file tool.

context_file_max_chars: 20000  # default

Raise it when you intentionally keep larger identity or project-context files and run models with enough context window to carry them:

context_file_max_chars: 25000

File Read Safety

Controls how much content a single read_file call can return. Reads that exceed the limit are rejected with an error telling the agent to use offset and limit for a smaller range. This prevents a single read of a minified JS bundle or large data file from flooding the context window.

file_read_max_chars: 100000  # default — ~25-35K tokens

Raise it if you’re on a model with a large context window and frequently read big files. Lower it for small-context models to keep reads efficient:

# Large context model (200K+)
file_read_max_chars: 200000

# Small local model (16K context)
file_read_max_chars: 30000

The agent also deduplicates file reads automatically — if the same file region is read twice and the file hasn’t changed, a lightweight stub is returned instead of re-sending the content. This resets on context compression so the agent can re-read files after their content is summarized away.

Tool Output Truncation Limits

Three related caps control how much raw output a tool can return before Hermes truncates it:

tool_output:
  max_bytes: 50000        # terminal output cap (chars)
  max_lines: 2000         # read_file pagination cap
  max_line_length: 2000   # per-line cap in read_file's line-numbered view
  • max_bytes — When a terminal command produces more than this many characters of combined stdout/stderr, Hermes keeps the first 40% and last 60% and inserts a [OUTPUT TRUNCATED] notice between them. Default 50000 (≈12-15K tokens across typical tokenisers).
  • max_lines — Upper bound on the limit parameter of a single read_file call. Requests above this are clamped so a single read can’t flood the context window. Default 2000.
  • max_line_length — Per-line cap applied when read_file emits the line-numbered view. Lines longer than this are truncated to this many chars followed by ... [truncated]. Default 2000.

Raise the limits on models with large context windows that can afford more raw output per call. Lower them for small-context models to keep tool results compact:

# Large context model (200K+)
tool_output:
  max_bytes: 150000
  max_lines: 5000

# Small local model (16K context)
tool_output:
  max_bytes: 20000
  max_lines: 500

Global Toolset Disable

To suppress specific toolsets across the CLI and every gateway platform in one place, list their names under agent.disabled_toolsets:

agent:
  disabled_toolsets:
    - memory       # hide memory tools + MEMORY_GUIDANCE injection
    - web          # no web_search / web_extract anywhere

This applies after per-platform tool config (platform_toolsets written by hermes tools), so a toolset listed here is always removed — even if a platform’s saved config still lists it. Use this when you want a single switch for “turn X off everywhere” rather than editing 15+ platform rows in the hermes tools UI.

Leaving the list empty, or omitting the key, is a no-op.

Git Worktree Isolation

Enable isolated git worktrees for running multiple agents in parallel on the same repo:

worktree: true    # Always create a worktree (same as hermes -w)
# worktree: false # Default — only when -w flag is passed

When enabled, each CLI session creates a fresh worktree under .worktrees/ with its own branch. Agents can edit files, commit, push, and create PRs without interfering with each other. Clean worktrees are removed on exit; dirty ones are kept for manual recovery.

By default the new worktree branches from the freshly-fetched remote tip (the current branch’s upstream, otherwise the remote’s default branch) so it starts current with the project rather than from the local clone’s possibly-stale HEAD. This keeps a PR’s diff scoped to the actual change instead of inheriting whatever the local clone was behind by. Set worktree_sync: false to branch from local HEAD instead — useful offline, or when you deliberately want the clone’s exact current state as the base. If the remote can’t be reached, it falls back to local HEAD automatically.

worktree_sync: true    # Default — branch from the fetched remote tip
# worktree_sync: false # Branch from local HEAD (offline / pinned base)

You can also list gitignored files to copy into worktrees via .worktreeinclude in your repo root:

# .worktreeinclude
.env
.venv/
node_modules/

Context Compression

Hermes automatically compresses long conversations to stay within your model’s context window. The compression summarizer is a separate LLM call — you can point it at any provider or endpoint.

All compression settings live in config.yaml (no environment variables).

Full reference

compression:
  enabled: true                                     # Toggle compression on/off
  threshold: 0.50                                   # Compress at this % of context limit
  target_ratio: 0.20                                # Fraction of threshold to preserve as recent tail
  protect_last_n: 20                                # Min recent messages to keep uncompressed
  protect_first_n: 3                                # Non-system head messages pinned across compactions (0 = pin nothing)
  hygiene_hard_message_limit: 5000                  # Gateway safety valve — see below

# The summarization model/provider is configured under auxiliary:
auxiliary:
  compression:
    model: ""                                       # Empty = use main chat model. Override with e.g. "google/gemini-3-flash-preview" for cheaper/faster compression.
    provider: "auto"                                # Provider: "auto", "openrouter", "nous", "codex", "main", etc.
    base_url: null                               


<!-- EXTRACT: # Messaging Gateway -->
# Messaging Gateway

# Messaging Gateway

Chat with Hermes from Telegram, Discord, Slack, WhatsApp, Signal, SMS, Email, Home Assistant, Mattermost, Matrix, DingTalk, Feishu/Lark, WeCom, Weixin, BlueBubbles (iMessage), QQ, Yuanbao, Microsoft Teams, LINE, ntfy, or your browser. The gateway is a single background process that connects to all your configured platforms, handles sessions, runs cron jobs, and delivers voice messages.

For the full voice feature set — including CLI microphone mode, spoken replies in messaging, and Discord voice-channel conversations — see [Voice Mode](/user-guide/features/voice-mode) and [Use Voice Mode with Hermes](/guides/use-voice-mode-with-hermes).

:::tip
Bots need both a model provider and tool providers (TTS, web). A [Nous Portal](/integrations/nous-portal) subscription bundles all of them.
:::

## Platform Comparison

| Platform | Voice | Images | Files | Threads | Reactions | Typing | Streaming |
|----------|:-----:|:------:|:-----:|:-------:|:---------:|:------:|:---------:|
| Telegram | ✅ | ✅ | ✅ | ✅ | — | ✅ | ✅ |
| Discord | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Slack | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Google Chat | — | ✅ | ✅ | ✅ | — | ✅ | — |
| WhatsApp | — | ✅ | ✅ | — | — | ✅ | ✅ |
| Signal | — | ✅ | ✅ | — | — | ✅ | ✅ |
| SMS | — | — | — | — | — | — | — |
| Email | — | ✅ | ✅ | ✅ | — | — | — |
| Home Assistant | — | — | — | — | — | — | — |
| Mattermost | ✅ | ✅ | ✅ | ✅ | — | ✅ | ✅ |
| Matrix | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| DingTalk | — | ✅ | ✅ | — | ✅ | — | ✅ |
| Feishu/Lark | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| WeCom | ✅ | ✅ | ✅ | — | — | — | — |
| WeCom Callback | — | — | — | — | — | — | — |
| Weixin | ✅ | ✅ | ✅ | — | — | ✅ | ✅ |
| BlueBubbles | — | ✅ | ✅ | — | ✅ | ✅ | — |
| QQ | ✅ | ✅ | ✅ | — | — | ✅ | — |
| Yuanbao | ✅ | ✅ | ✅ | — | — | ✅ | ✅ |
| Microsoft Teams | — | ✅ | — | ✅ | — | ✅ | — |
| LINE | — | ✅ | ✅ | — | — | ✅ | — |
| ntfy | — | — | — | — | — | — | — |
| Raft | — | — | — | — | — | — | — |
| IRC | — | — | — | — | — | — | — |

**Voice** = TTS audio replies and/or voice message transcription. **Images** = send/receive images. **Files** = send/receive file attachments. **Threads** = threaded conversations. **Reactions** = emoji reactions on messages. **Typing** = typing indicator while processing. **Streaming** = progressive message updates via editing.

## Architecture

```mermaid
flowchart TB
    subgraph Gateway["Hermes Gateway"]
        subgraph Adapters["Platform adapters"]
            tg[Telegram]
            dc[Discord]
            wa[WhatsApp]
            sl[Slack]
            gc[Google Chat]
            sig[Signal]
            sms[SMS]
            em[Email]
            ha[Home Assistant]
            mm[Mattermost]
            mx[Matrix]
            dt[DingTalk]
    fs[Feishu/Lark]
    wc[WeCom]
    wcb[WeCom Callback]
    wx[Weixin]
    bb[BlueBubbles]
    qq[QQ]
    yb[Yuanbao]
    ms[Microsoft Teams]
    api["API Server<br/>(OpenAI-compatible)"]
    wh[Webhooks]
        end

        store["Session store<br/>per chat"]
        agent["AIAgent<br/>run_agent.py"]
        cron["Cron scheduler<br/>ticks every 60s"]
    end

    tg --> store
    dc --> store
    wa --> store
    sl --> store
    gc --> store
    sig --> store
    sms --> store
    em --> store
    ha --> store
    mm --> store
    mx --> store
    dt --> store
    fs --> store
    wc --> store
    wcb --> store
    wx --> store
    bb --> store
    qq --> store
    yb --> store
    ms --> store
    api --> store
    wh --> store
    store --> agent
    cron --> store

Each platform adapter receives messages, routes them through a per-chat session store, and dispatches them to the AIAgent for processing. The gateway also runs the cron scheduler, ticking every 60 seconds to execute any due jobs.

Intentional Silence Tokens

For group chats, hooks, and automation flows, Hermes supports explicit silence tokens. If the agent’s final response is exactly one supported token, the gateway suppresses outbound delivery and sends nothing to the chat.

Supported tokens:

  • [SILENT]
  • SILENT
  • NO_REPLY
  • NO REPLY

Whitespace and case are normalized, but the whole final response must be the token. A sentence like “Use [SILENT] when nothing changed” is delivered normally.

Silence is a delivery decision only. Hermes keeps the assistant silence turn in the session transcript, so the conversation still alternates normally:

user: side-channel chatter
assistant: [SILENT]   # stored, not delivered
user: next message

Failed turns still surface as errors; Hermes does not hide failures just because the text resembles a silence token.

Quick Setup

The easiest way to configure messaging platforms is the interactive wizard:

hermes gateway setup        # Interactive setup for all messaging platforms

This walks you through configuring each platform with arrow-key selection, shows which platforms are already configured, and offers to start/restart the gateway when done.

Gateway Commands

hermes gateway              # Run in foreground
hermes gateway setup        # Configure messaging platforms interactively
hermes gateway install      # Install as a user service (Linux) / launchd service (macOS)
sudo hermes gateway install --system   # Linux only: install a boot-time system service
hermes gateway start        # Start the default service
hermes gateway stop         # Stop the default service
hermes gateway status       # Check default service status
hermes gateway status --system         # Linux only: inspect the system service explicitly

Chat Commands (Inside Messaging)

CommandDescription
/new or /resetStart a fresh conversation
/model [provider:model]Show or change the model (supports provider:model syntax)
/personality [name]Set a personality
/retryRetry the last message
/undoRemove the last exchange
/statusShow session info
/whoamiShow your slash command access on this scope (admin / user / unrestricted)
/stopStop the running agent
/approveApprove a pending dangerous command
/denyReject a pending dangerous command
/sethomeSet this chat as the home channel
/compressManually compress conversation context
/title [name]Set or show the session title
/resume [name]Resume a previously named session
/usageShow token usage for this session
/insights [days]Show usage insights and analytics
/reasoning [level|show|hide]Change reasoning effort or toggle reasoning display
/voice [on|off|tts|join|leave|status]Control messaging voice replies and Discord voice-channel behavior
/rollback [number]List or restore filesystem checkpoints
/background <prompt>Run a prompt in a separate background session
/reload-mcpReload MCP servers from config
/updateUpdate Hermes Agent to the latest version
/helpSho

Providers

Current provider families include (see plugins/model-providers/ for the complete bundled set):

  • OpenRouter
  • Nous Portal
  • OpenAI Codex
  • Copilot / Copilot ACP
  • Anthropic (native)
  • Google / Gemini (gemini)
  • Alibaba / DashScope (alibaba, alibaba-coding-plan)
  • DeepSeek
  • Z.AI
  • Kimi / Moonshot (kimi-coding, kimi-coding-cn)
  • MiniMax (minimax, minimax-cn, minimax-oauth)
  • Kilo Code
  • Hugging Face
  • OpenCode Zen / OpenCode Go
  • AWS Bedrock
  • Azure Foundry
  • NVIDIA NIM
  • xAI (Grok)
  • Arcee
  • GMI Cloud
  • StepFun
  • Qwen OAuth
  • Xiaomi
  • Ollama Cloud
  • LM Studio
  • Tencent TokenHub
  • Custom (provider: custom) — first-class provider for any OpenAI-compatible endpoint
  • Named custom providers (custom_providers list in config.yaml)

Output of runtime resolution

The runtime resolver returns data such as:

  • provider
  • api_mode
  • base_url
  • api_key
  • source
  • provider-specific metadata like expiry/refresh info

Why this matters

This resolver is the main reason Hermes can share auth/runtime logic between:

  • hermes chat
  • gateway message handling
  • cron jobs running in fresh sessions
  • ACP editor sessions
  • auxiliary model tasks

OpenRouter and custom OpenAI-compatible base URLs

Hermes contains logic to avoid leaking the wrong API key to a custom endpoint when multiple provider keys exist (e.g. OPENROUTER_API_KEY and OPENAI_API_KEY).

Each provider’s API key is scoped to its own base URL:

  • OPENROUTER_API_KEY is only sent to openrouter.ai endpoints
  • OPENAI_API_KEY is used for custom endpoints and as a fallback

Hermes also distinguishes between:

  • a real custom endpoint selected by the user
  • the OpenRouter fallback path used when no custom endpoint is configured

That distinction is especially important for:

  • local model servers
  • non-OpenRouter OpenAI-compatible APIs
  • switching providers without re-running setup
  • config-saved custom endpoints that should keep working even when OPENAI_BASE_URL is not exported in the current shell

Native Anthropic path

Anthropic is not just “via OpenRouter” anymore.

When provider resolution selects anthropic, Hermes uses:

  • api_mode = anthropic_messages
  • the native Anthropic Messages API
  • agent/anthropic_adapter.py for translation

Credential resolution for native Anthropic now prefers refreshable Claude Code credentials over copied env tokens when both are present. In practice that means:

  • Claude Code credential files are treated as the preferred source when they include refreshable auth
  • manual ANTHROPIC_TOKEN / CLAUDE_CODE_OAUTH_TOKEN values still work as explicit overrides
  • Hermes preflights Anthropic credential refresh before native Messages API calls
  • Hermes still retries once on a 401 after rebuilding the Anthropic client, as a fallback path

OpenAI Codex path

Codex uses a separate Responses API path:

  • api_mode = codex_responses
  • dedicated credential resolution and auth store support

Auxiliary model routing

Auxiliary tasks such as:

  • vision
  • web extraction summarization
  • context compression summaries
  • skills hub operations
  • MCP helper operations
  • memory flushes

can use their own provider/model routing rather than the main conversational model.

When an auxiliary task is configured with provider main, Hermes resolves that through the same shared runtime path as normal chat. In practice that means:

  • env-driven custom endpoints still work
  • custom endpoints saved via hermes model / config.yaml also work
  • auxiliary routing can tell the difference between a real saved custom endpoint and the OpenRouter fallback

Fallback models

Hermes supports a configured fallback provider chain — a list of (provider, model) entries tried in order when the primary model encounters errors. The legacy single-pair fallback_model dict is still accepted for back-compat (and migrated on first write).

How it works internally

  1. Storage: AIAgent.__init__ stores the fallback_model dict and sets _fallback_activated = False.

  2. Trigger points: _try_activate_fallback() is called from three places in the main retry loop in run_agent.py:

    • After max retries on invalid API responses (None choices, missing content)
    • On non-retryable client errors (HTTP 401, 403, 404)
    • After max retries on transient errors (HTTP 429, 500, 502, 503)
  3. Activation flow (_try_activate_fallback):

    • Returns False immediately if already activated or not configured
    • Calls resolve_provider_client() from auxiliary_client.py to build a new client with proper auth
    • Determines api_mode: codex_responses for openai-codex, anthropic_messages for anthropic, chat_completions for everything else
    • Swaps in-place: self.model, self.provider, self.base_url, self.api_mode, self.client, self._client_kwargs
    • For anthropic fallback: builds a native Anthropic client instead of OpenAI-compatible
    • Re-evaluates prompt caching (enabled for Claude models on OpenRouter)
    • Sets _fallback_activated = True — prevents firing again
    • Resets retry count to 0 and continues the loop
  4. Config flow:

    • CLI: cli.py reads CLI_CONFIG["fallback_model"] → passes to AIAgent(fallback_model=...)
    • Gateway: gateway/run.py._load_fallback_model() reads config.yaml → passes to AIAgent
    • Validation: both provider and model keys must be non-empty, or fallback is disabled

What does NOT support fallback

  • Subagent delegation (tools/delegate_tool.py): subagents inherit the parent’s provider but not the fallback config
  • Auxiliary tasks: use their own independent provider auto-detection chain (see Auxiliary model routing above)

Cron jobs do support fallback: run_job() reads fallback_providers (or legacy fallback_model) from config.yaml and passes it to AIAgent(fallback_model=...), matching the gateway’s _load_fallback_model() pattern. See Cron Internals.

Test coverage

Fallback behavior is exercised across several suites:

  • tests/run_agent/test_fallback_credential_isolation.py — credential isolation between primary and fallback
  • tests/hermes_cli/test_fallback_cmd.py — the /fallback CLI command
  • tests/gateway/test_fallback_eviction.py — gateway eviction of failed providers

Security

Pre-execution security scanning and secret redaction:

security:
  redact_secrets: true           # Redact API key patterns in tool output and logs (on by default)
  tirith_enabled: true           # Enable Tirith security scanning for terminal commands
  tirith_path: "tirith"          # Path to tirith binary (default: "tirith" in $PATH)
  tirith_timeout: 5              # Seconds to wait for tirith scan before timing out
  tirith_fail_open: true         # Allow command execution if tirith is unavailable
  website_blocklist:             # See Website Blocklist section below
    enabled: false
    domains: []
    shared_files: []
  • redact_secrets — when true, automatically detects and redacts patterns that look like API keys, tokens, and passwords in tool output before it enters the conversation context and logs. On by default. Set to false explicitly only when you need raw credential-like strings for debugging or redactor development.
  • tirith_enabled — when true, terminal commands are scanned by Tirith before execution to detect potentially dangerous operations.
  • tirith_path — path to the tirith binary. Set this if tirith is installed in a non-standard location.
  • tirith_timeout — maximum seconds to wait for a tirith scan. Commands proceed if the scan times out.
  • tirith_fail_open — when true (default), commands are allowed to execute if tirith is unavailable or fails. Set to false to block commands when tirith cannot verify them.

Website Blocklist

Block specific domains from being accessed by the agent’s web and browser tools:

security:
  website_blocklist:
    enabled: false               # Enable URL blocking (default: false)
    domains:                     # List of blocked domain patterns
      - "*.internal.company.com"
      - "admin.example.com"
      - "*.local"
    shared_files:                # Load additional rules from external files
      - "/etc/hermes/blocked-sites.txt"

When enabled, any URL matching a blocked domain pattern is rejected before the web or browser tool executes. This applies to web_search, web_extract, browser_navigate, and any tool that accesses URLs.

Domain rules support:

  • Exact domains: admin.example.com
  • Wildcard subdomains: *.internal.company.com (blocks all subdomains)
  • TLD wildcards: *.local

Shared files contain one domain rule per line (blank lines and # comments are ignored). Missing or unreadable files log a warning but don’t disable other web tools.

The policy is cached for 30 seconds, so config changes take effect quickly without restart.

Smart Approvals

Control how Hermes handles potentially dangerous commands:

approvals:
  mode: manual   # manual | smart | off
ModeBehavior
manual (default)Prompt the user before executing any flagged command. In the CLI, shows an interactive approval dialog. In messaging, queues a pending approval request.
smartUse an auxiliary LLM to assess whether a flagged command is actually dangerous. Low-risk commands are auto-approved with session-level persistence. Genuinely risky commands are escalated to the user.
offSkip all approval checks. Equivalent to HERMES_YOLO_MODE=true. Use with caution.

Smart mode is particularly useful for reducing approval fatigue — it lets the agent work more autonomously on safe operations while still catching genuinely destructive commands.

:::warning Setting approvals.mode: off disables all safety checks for terminal commands. Only use this in trusted, sandboxed environments. :::

Deny rules

approvals.deny is a list of glob patterns that block matching terminal commands unconditionally — even under --yolo, /yolo, or mode: off. It’s the user-editable counterpart to the built-in hardline blocklist:

approvals:
  deny:
    - "git push --force*"
    - "*curl*|*sh*"

Patterns are case-insensitive fnmatch globs and must be quoted in YAML (a bare leading * is a parse error). See Security — User-Defined Deny Rules for details.

Checkpoints

Automatic filesystem snapshots before destructive file operations. See the Checkpoints & Rollback for details.

checkpoints:
  enabled: false                 # Enable automatic checkpoints (also: hermes chat --checkpoints). Default: false (opt-in).
  max_snapshots: 20              # Max checkpoints to keep per directory (default: 20)

Delegation

Configure subagent behavior for the delegate tool:

delegation:
  # model: "google/gemini-3-flash-preview"  # Override model (empty = inherit parent)
  # provider: "openrouter"                  # Override provider (empty = inherit parent)
  # base_url: "http://localhost:1234/v1"    # Direct OpenAI-compatible endpoint (takes precedence over provider)
  # api_key: "local-key"                    # API key for base_url (falls back to OPENAI_API_KEY)
  # api_mode: ""                            # Wire protocol for base_url: "chat_completions", "codex_responses", or "anthropic_messages". Empty = auto-detect from URL (e.g. /anthropic suffix → anthropic_messages). Set explicitly for non-standard endpoints the heuristic can't detect.
  max_concurrent_children: 3                # Parallel children per batch (floor 1, no ceiling). Also via DELEGATION_MAX_CONCURRENT_CHILDREN env var.
  max_spawn_depth: 1                        # Delegation tree depth cap (1-3, clamped). 1 = flat (default): parent spawns leaves that cannot delegate. 2 = orchestrator children can spawn leaf grandchildren. 3 = three levels.
  orchestrator_enabled: true                # Global kill switch. When false, role="orchestrator" is ignored and every child is forced to leaf regardless of max_spawn_depth.

Subagent provider:model override: By default, subagents inherit the parent agent’s provider and model. Set delegation.provider and delegation.model to route subagents to a different provider:model pair — e.g., use a cheap/fast model for narrowly-scoped subtasks while your primary agent runs an expensive reasoning model.

Direct endpoint override: If you want the obvious custom-endpoint path, set delegation.base_url, delegation.api_key, and delegation.model. That sends subagents directly to that OpenAI-compatible endpoint and takes precedence over delegation.provider. If delegation.api_key is omitted, Hermes falls back to OPENAI_API_KEY only.

Wire protocol (api_mode): Hermes auto-detects the wire protocol from delegation.base_url (e.g. paths ending in /anthropicanthropic_messages; Codex / native Anthropic / Kimi-coding hostnames keep their existing detection). For endpoints the heuristic can’t classify — for example Azure AI Foundry, MiniMax, Zhipu GLM, or LiteLLM proxies fronting an Anthropic-shaped backend — set delegation.api_mode