2148 lines
111 KiB
Plaintext
2148 lines
111 KiB
Plaintext
# Hermes Agent CLI Configuration
|
||
# Copy settings from this example into ~/.hermes/config.yaml, or use
|
||
# `hermes config set <section.key> <value>` to update the active profile.
|
||
# This file configures CLI behavior; only documented secret environment
|
||
# variables in .env take precedence over their corresponding settings.
|
||
|
||
# =============================================================================
|
||
# Database Configuration
|
||
# =============================================================================
|
||
# WAL is the normal default. Hermes automatically falls back to DELETE when
|
||
# SQLite reports that WAL is incompatible with the filesystem. Set this to
|
||
# "delete" explicitly for deployments whose backing filesystem is not WAL
|
||
# crash-safe, such as Linux containers bind-mounted through macOS virtiofs,
|
||
# NFS, or SMB. Hermes will not live-downgrade a database already open in WAL.
|
||
database:
|
||
journal_mode: "wal" # Supported values: "wal", "delete"
|
||
# Optional WAL sizing pragmas (integers). Unset = SQLite defaults.
|
||
# wal_autocheckpoint: 1000 # pages between automatic checkpoints
|
||
# journal_size_limit: 67108864 # cap the WAL/journal file size in bytes
|
||
#
|
||
# Durability level for every state.db connection: OFF, NORMAL, FULL, EXTRA
|
||
# (or 0-3). Unset leaves SQLite's default, which is baked in at compile time
|
||
# (SQLITE_DEFAULT_WAL_SYNCHRONOUS) and therefore differs between the bundled
|
||
# interpreter, a distro python3 and a Homebrew one. Set it if you need to
|
||
# know which one you are running rather than infer it. On macOS this is a
|
||
# floor, not a pin: values below FULL are refused because Darwin's fsync()
|
||
# does not guarantee write ordering, while EXTRA is honored normally.
|
||
# synchronous: FULL
|
||
|
||
# =============================================================================
|
||
# Runtime Limits
|
||
# =============================================================================
|
||
# Long-running Hermes server processes raise their RLIMIT_NOFILE soft limit to
|
||
# this value when the operating system permits it. The value is clamped to the
|
||
# hard limit and never lowers an already higher soft limit. Set to 0, false, or
|
||
# null to disable the adjustment. Default: 4096.
|
||
runtime:
|
||
nofile_soft_limit: 4096
|
||
|
||
# =============================================================================
|
||
# Model Configuration
|
||
# =============================================================================
|
||
model:
|
||
# Default model to use (can be overridden with --model flag)
|
||
# Both "default" and "model" work as the key name here.
|
||
default: "anthropic/claude-opus-4.6"
|
||
|
||
# Inference provider selection:
|
||
# "auto" - Auto-detect from credentials (default)
|
||
# "openrouter" - OpenRouter (requires: OPENROUTER_API_KEY or OPENAI_API_KEY)
|
||
# "nous" - Nous Portal OAuth (requires: hermes auth add nous)
|
||
# "nous-api" - Nous Portal API key (requires: NOUS_API_KEY)
|
||
# "anthropic" - Direct Anthropic API (requires: ANTHROPIC_API_KEY)
|
||
# "openai-codex" - OpenAI Codex (requires: hermes auth)
|
||
# "copilot" - GitHub Copilot / GitHub Models (requires: GITHUB_TOKEN)
|
||
# "gemini" - Use Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)
|
||
# "zai" - Use z.ai / ZhipuAI GLM models (requires: GLM_API_KEY)
|
||
# "kimi-coding" - Kimi / Moonshot AI (requires: KIMI_API_KEY)
|
||
# "minimax" - MiniMax global (requires: MINIMAX_API_KEY)
|
||
# "minimax-cn" - MiniMax China (requires: MINIMAX_CN_API_KEY)
|
||
# "huggingface" - Hugging Face Inference (requires: HF_TOKEN)
|
||
# "nvidia" - NVIDIA NIM / build.nvidia.com (requires: NVIDIA_API_KEY)
|
||
# "xiaomi" - Xiaomi MiMo (requires: XIAOMI_API_KEY)
|
||
# "arcee" - Arcee AI Trinity models (requires: ARCEEAI_API_KEY)
|
||
# "ollama-cloud" - Ollama Cloud (requires: OLLAMA_API_KEY — https://ollama.com/settings)
|
||
# "deepinfra" - DeepInfra (requires: DEEPINFRA_API_KEY)
|
||
# "kilocode" - KiloCode gateway (requires: KILOCODE_API_KEY)
|
||
# "ai-gateway" - Vercel AI Gateway (requires: AI_GATEWAY_API_KEY)
|
||
# "azure-foundry" - Microsoft Foundry / Azure OpenAI (API key or Entra ID)
|
||
# "lmstudio" - LM Studio local server (optional: LM_API_KEY, defaults to http://127.0.0.1:1234/v1)
|
||
#
|
||
# Local servers (LM Studio, Ollama, vLLM, llama.cpp):
|
||
# "custom" - Any other OpenAI-compatible endpoint. Set base_url below.
|
||
# Aliases: "ollama", "vllm", "llamacpp" all map to "custom".
|
||
# LM Studio is first-class and uses provider: "lmstudio".
|
||
# It works with both no-auth and auth-enabled server modes.
|
||
#
|
||
# Can also be overridden for a single invocation with the --provider flag.
|
||
provider: "auto"
|
||
|
||
# API configuration (falls back to OPENROUTER_API_KEY env var)
|
||
# api_key: "your-key-here" # Uncomment to set here instead of .env
|
||
base_url: "https://openrouter.ai/api/v1"
|
||
|
||
# Stream API responses from the provider (default: true). The agent core
|
||
# prefers streaming for every turn — subagents included — for liveness
|
||
# health-checking. Set false to force non-streaming requests for the whole
|
||
# session (persists across mid-session model switches). Escape hatch for
|
||
# self-hosted OpenAI-compatible servers whose streaming tool-call path is
|
||
# broken (e.g. vLLM with --tool-call-parser qwen3_xml + a reasoning parser
|
||
# can leak tool calls into plain text instead of returning tool_calls —
|
||
# #72901). Orthogonal to display.streaming, which controls token rendering
|
||
# only.
|
||
# streaming: true
|
||
|
||
# Azure Foundry keyless auth example:
|
||
# provider: "azure-foundry"
|
||
# base_url: "https://<resource>.openai.azure.com/openai/v1"
|
||
# auth_mode: "entra_id" # DefaultAzureCredential: az login, managed identity, workload identity, etc.
|
||
# default: "gpt-4o" # Deployment/model name
|
||
# entra:
|
||
# scope: "https://ai.azure.com/.default" # Optional; this is the default.
|
||
|
||
# ── Token limits — two settings, easy to confuse ──────────────────────────
|
||
#
|
||
# context_length: TOTAL context window (input + output tokens combined).
|
||
# Controls when Hermes compresses history and validates requests.
|
||
# Leave unset — Hermes auto-detects the correct value from the provider.
|
||
# Set manually only when auto-detection is wrong (e.g. a local server with
|
||
# a custom num_ctx, or a proxy that doesn't expose /v1/models).
|
||
#
|
||
# context_length: 131072
|
||
#
|
||
# max_tokens: OUTPUT cap — maximum tokens the model may generate per response.
|
||
# Unrelated to how long your conversation history can be.
|
||
# The OpenAI-standard name "max_tokens" is a misnomer; Anthropic's native
|
||
# API has since renamed it "max_output_tokens" for clarity.
|
||
# Leave unset to use the model's native output ceiling (recommended).
|
||
# Set only if you want to deliberately limit individual response length.
|
||
#
|
||
# max_tokens: 8192
|
||
|
||
# ── Custom request headers (optional) ─────────────────────────────────────
|
||
#
|
||
# default_headers: extra HTTP headers sent on every request to an
|
||
# OpenAI-compatible endpoint. User values take precedence over the
|
||
# provider/SDK defaults, so this is the supported way to override the
|
||
# OpenAI Python SDK's identifying headers (User-Agent: OpenAI/Python ...,
|
||
# X-Stainless-*) when a custom provider sits behind a gateway/WAF that
|
||
# rejects them — e.g. an upstream that returns "502 Upstream access
|
||
# forbidden" for the SDK default User-Agent but accepts a plain one.
|
||
# Applies on the OpenAI wire only (not native Anthropic / Bedrock).
|
||
#
|
||
# default_headers:
|
||
# User-Agent: "curl/8.7.1"
|
||
#
|
||
# extra_headers: accepted as an alias of default_headers (merged, with
|
||
# extra_headers winning when both are set) — matches the per-provider
|
||
# extra_headers key below.
|
||
#
|
||
# Per-provider variant: named providers / custom_providers entries accept an
|
||
# extra_headers dict scoped to that endpoint only — for reverse proxies,
|
||
# gateways, or custom auth (e.g. Cloudflare Access service tokens).
|
||
# Merged onto SDK/provider defaults with the entry's values winning.
|
||
# Header values are treated as secrets and are never logged.
|
||
#
|
||
# providers:
|
||
# my-proxy:
|
||
# base_url: "https://llm.internal.example.com/v1"
|
||
# key_env: "MY_PROXY_API_KEY"
|
||
# extra_headers:
|
||
# CF-Access-Client-Id: "xxxx.access"
|
||
# CF-Access-Client-Secret: "${CF_ACCESS_SECRET}"
|
||
# X-Client-Name: "hermes-agent"
|
||
# providers:
|
||
# meta:
|
||
# base_url: https://api.meta.ai/v1
|
||
# api_key: ${MODEL_API_KEY}
|
||
# # api_mode auto-detected as codex_responses for api.meta.ai; no need to set
|
||
# # (the bundled meta-ai provider covers this — a named custom provider is
|
||
# # only needed for a non-default Meta-compatible endpoint)
|
||
# providers:
|
||
# router:
|
||
# base_url: https://api.router.com/v1
|
||
# api_key: ${RAMP_ROUTER_API_KEY}
|
||
# # api_mode auto-detected as codex_responses for api.router.com — the
|
||
# # Responses API is Router's native wire (chat/completions is only a
|
||
# # compatibility shim). The bundled router provider covers this; a named
|
||
# # custom provider is only needed for a non-default Router-compatible
|
||
# # endpoint.
|
||
|
||
|
||
# Command-minted credentials (optional): key_cmd
|
||
# ------------------------------------------------------------------
|
||
# Enterprise gateways often issue SHORT-LIVED bearers (SSO/OIDC brokers, cloud
|
||
# IAM, internal auth proxies) rather than static API keys, so a value copied
|
||
# into .env via `key_env` is stale within the hour and every later request 401s.
|
||
# `key_cmd` names a command that PRINTS a token instead: Hermes runs it per
|
||
# request (cached until shortly before expiry), so long sessions keep working
|
||
# with no restart.
|
||
#
|
||
# Contract: print ONLY the token on stdout, either bare or as JSON with an
|
||
# "access_token" field ("expires_in" is honoured). Same shape as OAuth 2.0
|
||
# token endpoints, Claude Code's `apiKeyHelper`, `gcloud auth
|
||
# print-access-token`, and `aws ecr get-login-password`.
|
||
#
|
||
# Precedence: an explicit --api-key still wins; otherwise key_cmd is preferred
|
||
# over inline api_key / key_env on that entry.
|
||
#
|
||
# Applies to the main agent turn and to auxiliary tasks (title generation,
|
||
# context compression, vision, embedding) alike.
|
||
#
|
||
# Not to be confused with `secrets.command`, which is a different mechanism:
|
||
# that one runs a helper ONCE at startup to populate env vars for many secrets
|
||
# at the process level. Use it for a vault or keychain helper that hands back a
|
||
# KEY=VALUE blob. Use `key_cmd` when ONE provider needs a credential refreshed
|
||
# DURING a session, because a startup-time env var cannot be re-minted after it
|
||
# expires.
|
||
#
|
||
# providers:
|
||
# my-gateway:
|
||
# base_url: "https://gateway.internal.example.com/v1"
|
||
# api_mode: chat_completions
|
||
# key_cmd: "my-auth-cli print-token --profile prod"
|
||
#
|
||
# Worked example — an AI gateway that routes by model family, so one entry per
|
||
# wire format shares the same credential helper:
|
||
#
|
||
# providers:
|
||
# dbx: # OpenAI-compatible (MLflow) route
|
||
# base_url: "https://<workspace>.cloud.databricks.com/ai-gateway/mlflow/v1"
|
||
# api_mode: chat_completions
|
||
# model: databricks-claude-sonnet-4-6
|
||
# key_cmd: "databricks auth token -p MY-PROFILE"
|
||
# dbx-gpt: # OpenAI Responses route
|
||
# base_url: "https://<workspace>.cloud.databricks.com/ai-gateway/openai/v1"
|
||
# api_mode: codex_responses
|
||
# model: databricks-gpt-5-5
|
||
# key_cmd: "databricks auth token -p MY-PROFILE"
|
||
# dbx-claude: # Anthropic Messages route
|
||
# base_url: "https://<workspace>.cloud.databricks.com/ai-gateway/anthropic"
|
||
# api_mode: anthropic_messages
|
||
# model: databricks-claude-fable-5
|
||
# key_cmd: "databricks auth token -p MY-PROFILE"
|
||
|
||
# Named provider overrides (optional)
|
||
# Use this for per-provider request timeouts, non-stream stale timeouts,
|
||
# and per-model exceptions.
|
||
# Applies to the primary turn client on every api_mode (OpenAI-wire, native
|
||
# Anthropic, and Anthropic-compatible providers), the fallback chain, and
|
||
# client rebuilds during credential rotation. For OpenAI-wire chat
|
||
# completions (streaming and non-streaming) the configured value is also
|
||
# used as the per-request ``timeout=`` kwarg so it wins over the legacy
|
||
# HERMES_API_TIMEOUT env var (which still applies when no config is set).
|
||
# ``stale_timeout_seconds`` controls the non-streaming stale-call detector and
|
||
# wins over the legacy HERMES_API_CALL_STALE_TIMEOUT env var. Leaving these
|
||
# unset keeps the legacy defaults (HERMES_API_TIMEOUT=1800s,
|
||
# HERMES_API_CALL_STALE_TIMEOUT=90s, native Anthropic 900s). The
|
||
# implicit non-stream stale detector is auto-disabled for local endpoints
|
||
# and can scale upward for very large contexts.
|
||
#
|
||
# Not currently wired for AWS Bedrock (bedrock_converse + AnthropicBedrock
|
||
# SDK paths) — those use boto3 with its own timeout configuration.
|
||
#
|
||
# providers:
|
||
# ollama-local:
|
||
# request_timeout_seconds: 300 # Longer timeout for local cold-starts
|
||
# stale_timeout_seconds: 900 # Explicitly re-enable stale detection on local endpoints
|
||
# anthropic:
|
||
# request_timeout_seconds: 30 # Fast-fail cloud requests
|
||
# models:
|
||
# claude-opus-4.6:
|
||
# timeout_seconds: 600 # Longer timeout for extended-thinking Opus calls
|
||
# openai-codex:
|
||
# models:
|
||
# gpt-5.4:
|
||
# stale_timeout_seconds: 1800 # Longer non-stream stale timeout for slow large-context turns
|
||
|
||
# =============================================================================
|
||
# Unified Timeouts (operation deadlines)
|
||
# =============================================================================
|
||
# One place to override Hermes's internal operation deadlines (seconds).
|
||
# Keys are dotted paths resolved by agent/deadline.py:resolve_timeout().
|
||
# Precedence: this section > legacy HERMES_* env var (back-compat) > built-in
|
||
# default. 0 or a negative value disables the bound (unbounded); very large
|
||
# values are clamped to a platform-safe maximum automatically.
|
||
#
|
||
# Currently resolved keys (more paths migrate here over time — see issue #85125):
|
||
#
|
||
# timeouts:
|
||
# tools:
|
||
# concurrent_batch: 420 # Deadline for a parallel tool-call batch
|
||
# # (legacy env: HERMES_CONCURRENT_TOOL_TIMEOUT_S)
|
||
# sequential_call: 420 # Deadline for one sequentially-executed tool call.
|
||
# # Defaults to concurrent_batch's value so the two
|
||
# # executor paths stay in sync; human waits
|
||
# # (approval prompts, clarify) never count against it.
|
||
|
||
# =============================================================================
|
||
# OpenRouter Provider Routing (only applies when using OpenRouter)
|
||
# =============================================================================
|
||
# Control how requests are routed across providers on OpenRouter.
|
||
# See: https://openrouter.ai/docs/guides/routing/provider-selection
|
||
#
|
||
# provider_routing:
|
||
# # Sort strategy: "price" (default), "throughput", or "latency"
|
||
# # Append :nitro to model name for a shortcut to throughput sorting.
|
||
# sort: "throughput"
|
||
#
|
||
# # Only allow these providers (provider slugs from OpenRouter)
|
||
# # only: ["anthropic", "google"]
|
||
#
|
||
# # Skip these providers entirely
|
||
# # ignore: ["deepinfra", "fireworks"]
|
||
#
|
||
# # Try providers in this order (overrides default load balancing)
|
||
# # order: ["anthropic", "google", "together"]
|
||
#
|
||
# # Require providers to support all parameters in your request
|
||
# # require_parameters: true
|
||
#
|
||
# # Data policy: "allow" (default) or "deny" to exclude providers that may store data
|
||
# # data_collection: "deny"
|
||
|
||
# =============================================================================
|
||
# OpenRouter Response Caching (only applies when using OpenRouter)
|
||
# =============================================================================
|
||
# Cache identical API responses at the OpenRouter edge for free instant replays.
|
||
# When enabled, identical requests (same model, messages, parameters) return
|
||
# cached responses with zero billing. Separate from Anthropic prompt caching.
|
||
# See: https://openrouter.ai/docs/guides/features/response-caching
|
||
#
|
||
# openrouter:
|
||
# response_cache: true # Enable response caching (default: true)
|
||
# response_cache_ttl: 300 # Cache TTL in seconds, 1-86400 (default: 300)
|
||
|
||
# =============================================================================
|
||
# Git Worktree Isolation
|
||
# =============================================================================
|
||
# When enabled, each CLI session creates an isolated git worktree so multiple
|
||
# agents can work on the same repo concurrently without file collisions.
|
||
# Equivalent to always passing --worktree / -w on the command line.
|
||
#
|
||
# worktree: true # Always create a worktree when in a git repo
|
||
# worktree: false # Default — only create when -w flag is passed
|
||
#
|
||
# By default a new worktree branches from the freshly-fetched remote tip
|
||
# (the current branch's upstream, else the remote's default branch) so it
|
||
# starts current with the project instead of from the local clone's
|
||
# (possibly stale) HEAD. Set worktree_sync: false to branch from local HEAD
|
||
# instead — useful when offline or when you deliberately want the clone's
|
||
# exact current state as the base.
|
||
#
|
||
# worktree_sync: true # Default — branch from the fetched remote tip
|
||
# worktree_sync: false # Branch from local HEAD (offline / pinned base)
|
||
|
||
# =============================================================================
|
||
# Kanban Review Dispatch
|
||
# =============================================================================
|
||
# First-class review tasks are dispatched automatically by default. The worker
|
||
# is spawned with the bundled sdlc-review skill and can approve, request changes
|
||
# back to the original implementer, or escalate a genuine external blocker.
|
||
# Disable this only when every review is performed manually from the dashboard.
|
||
kanban:
|
||
review_dispatch: true
|
||
|
||
# =============================================================================
|
||
# Terminal Tool Configuration
|
||
# =============================================================================
|
||
# Choose ONE of the following terminal configurations by uncommenting it.
|
||
# The terminal tool executes commands in the specified environment.
|
||
|
||
# -----------------------------------------------------------------------------
|
||
# OPTION 1: Local execution (default)
|
||
# Commands run directly on your machine in the current directory
|
||
# -----------------------------------------------------------------------------
|
||
# Working directory behavior:
|
||
# - CLI (`hermes` command): Uses "." (current directory where you run hermes)
|
||
# - Gateway/messaging/cron: Uses terminal.cwd here; legacy .env cwd values are deprecated
|
||
terminal:
|
||
backend: "local"
|
||
cwd: "." # For local backend: "." = current directory. Ignored for remote backends unless a backend documents otherwise.
|
||
# Desktop xterm font. Install the font locally; Nerd Fonts render Powerlevel10k glyphs.
|
||
# font_family: "MesloLGS NF" # Also accepts a CSS stack; blank uses bundled JetBrains Mono.
|
||
timeout: 180
|
||
# HOME policy for tool subprocesses:
|
||
# auto - default: host uses your real HOME; containers use HERMES_HOME/home
|
||
# real - force your real OS-user HOME
|
||
# profile - force HERMES_HOME/home for strict per-profile CLI config isolation
|
||
home_mode: "auto"
|
||
docker_mount_cwd_to_workspace: false # SECURITY: off by default. Opt in to mount the launch cwd into Docker /workspace.
|
||
lifetime_seconds: 300
|
||
# sudo_password: "hunter2" # Optional: pipe a sudo password via sudo -S. SECURITY WARNING: plaintext.
|
||
# sudo_password: "" # Explicit empty password: try empty and never open the interactive sudo prompt.
|
||
|
||
# -----------------------------------------------------------------------------
|
||
# OPTION 2: SSH remote execution
|
||
# Commands run on a remote server - agent code stays local (sandboxed)
|
||
# Great for: keeping agent isolated from its own code, using powerful remote hardware
|
||
# -----------------------------------------------------------------------------
|
||
# terminal:
|
||
# backend: "ssh"
|
||
# cwd: "/home/myuser/project" # Path on the REMOTE server
|
||
# timeout: 180
|
||
# lifetime_seconds: 300
|
||
# ssh_host: "my-server.example.com"
|
||
# ssh_user: "myuser"
|
||
# ssh_port: 22
|
||
# ssh_key: "~/.ssh/id_rsa" # Optional - uses ssh-agent if not specified
|
||
|
||
# -----------------------------------------------------------------------------
|
||
# OPTION 3: Docker container
|
||
# Commands run in an isolated Docker container
|
||
# Great for: reproducible environments, testing, isolation
|
||
# -----------------------------------------------------------------------------
|
||
# terminal:
|
||
# backend: "docker"
|
||
# cwd: "/workspace" # Path INSIDE the container (default: /)
|
||
# timeout: 180
|
||
# lifetime_seconds: 300
|
||
# docker_image: "nikolaik/python-nodejs:python3.11-nodejs20"
|
||
# docker_mount_cwd_to_workspace: true # Explicit opt-in: mount your launch cwd into /workspace
|
||
# # Optional: run the container as your host user's uid:gid so files written
|
||
# # into bind-mounted dirs are owned by you, not root. Drops SETUID/SETGID
|
||
# # caps too since no gosu privilege drop is needed. Leave off if your
|
||
# # chosen docker_image expects to start as root.
|
||
# docker_run_as_host_user: true
|
||
# # Optional: explicitly forward selected env vars into Docker.
|
||
# # These values come from your current shell first, then ~/.hermes/.env.
|
||
# # Warning: anything forwarded here is visible to commands run in the container.
|
||
# docker_forward_env:
|
||
# - "GITHUB_TOKEN"
|
||
# - "NPM_TOKEN"
|
||
# # Optional: extra flags passed verbatim to docker run (appended after security defaults).
|
||
# # Useful for adding capabilities (e.g. apt installs needing SETUID) or custom options.
|
||
# # Example: add a Linux capability not included by default
|
||
# # docker_extra_args:
|
||
# # - "--cap-add"
|
||
# # - "SETUID"
|
||
|
||
# -----------------------------------------------------------------------------
|
||
# OPTION 4: Singularity/Apptainer container
|
||
# Commands run in a Singularity container (common in HPC environments)
|
||
# Great for: HPC clusters, shared compute environments
|
||
# -----------------------------------------------------------------------------
|
||
# terminal:
|
||
# backend: "singularity"
|
||
# cwd: "/workspace" # Path INSIDE the container (default: /root)
|
||
# timeout: 180
|
||
# lifetime_seconds: 300
|
||
# singularity_image: "docker://nikolaik/python-nodejs:python3.11-nodejs20"
|
||
|
||
# -----------------------------------------------------------------------------
|
||
# OPTION 5: Modal cloud execution
|
||
# Commands run on Modal's cloud infrastructure
|
||
# Great for: GPU access, scalable compute, serverless execution
|
||
# -----------------------------------------------------------------------------
|
||
# terminal:
|
||
# backend: "modal"
|
||
# cwd: "/workspace" # Path INSIDE the sandbox (default: /root)
|
||
# timeout: 180
|
||
# lifetime_seconds: 300
|
||
# modal_image: "nikolaik/python-nodejs:python3.11-nodejs20"
|
||
|
||
# -----------------------------------------------------------------------------
|
||
# OPTION 6: Daytona cloud execution
|
||
# Commands run in Daytona cloud sandboxes
|
||
# Great for: Cloud dev environments, persistent workspaces, team collaboration
|
||
# Requires: pip install daytona, DAYTONA_API_KEY env var
|
||
# -----------------------------------------------------------------------------
|
||
# terminal:
|
||
# backend: "daytona"
|
||
# cwd: "~"
|
||
# timeout: 180
|
||
# lifetime_seconds: 300
|
||
# daytona_image: "nikolaik/python-nodejs:python3.11-nodejs20"
|
||
# container_disk: 10240 # Daytona max is 10GB per sandbox
|
||
|
||
#
|
||
# --- Container resource limits (docker, singularity, modal, daytona -- ignored for local/ssh) ---
|
||
# These settings apply to all container backends. They control the resources
|
||
# allocated to the sandbox and whether its filesystem persists across sessions.
|
||
container_cpu: 1 # CPU cores
|
||
container_memory: 5120 # Memory in MB (5120 = 5GB)
|
||
container_disk: 51200 # Disk in MB (51200 = 50GB)
|
||
container_persistent: true # Persist filesystem across sessions (false = ephemeral)
|
||
|
||
# -----------------------------------------------------------------------------
|
||
# SUDO SUPPORT (works with ALL backends above)
|
||
# -----------------------------------------------------------------------------
|
||
# Add sudo_password to any terminal config above to enable sudo commands.
|
||
# The password is piped via `sudo -S`. Works with local, ssh, docker, etc.
|
||
#
|
||
# SECURITY WARNING: Password stored in plaintext!
|
||
#
|
||
# INTERACTIVE PROMPT: If sudo_password is unset and the CLI is running,
|
||
# you'll be prompted to enter your password when sudo is needed:
|
||
# - 45-second timeout (auto-skips if no input)
|
||
# - Press Enter to skip (command fails gracefully)
|
||
# - Password is hidden while typing
|
||
# - Password is cached for the session
|
||
#
|
||
# EMPTY PASSWORDS: Setting sudo_password to an explicit empty string is different
|
||
# from leaving it unset. Hermes will try an empty password via `sudo -S` and
|
||
# will not open the interactive prompt. This is useful for passwordless sudo,
|
||
# Touch ID sudo setups, and environments where prompting is just noise.
|
||
#
|
||
# ALTERNATIVES:
|
||
# - SSH backend: Configure passwordless sudo on the remote server
|
||
# - Containers: Run as root inside the container (no sudo needed)
|
||
# - Local: Configure /etc/sudoers for specific commands
|
||
#
|
||
# Example (add to your terminal section):
|
||
# sudo_password: "your-password-here"
|
||
|
||
# =============================================================================
|
||
# Security Scanning (tirith)
|
||
# =============================================================================
|
||
# Optional pre-exec command security scanning via tirith.
|
||
# Detects homograph URLs, pipe-to-shell, terminal injection, env manipulation.
|
||
# Install: brew install sheeki03/tap/tirith
|
||
# Docs: https://github.com/sheeki03/tirith
|
||
#
|
||
# security:
|
||
# tirith_enabled: true # Enable/disable tirith scanning
|
||
# tirith_path: "tirith" # Path to tirith binary (supports ~ expansion)
|
||
# tirith_timeout: 5 # Scan timeout in seconds
|
||
# tirith_fail_open: true # Allow commands if tirith unavailable
|
||
# approval:
|
||
# transport: builtin # Or an explicitly enabled plugin transport name
|
||
# transport_fallback: deny # Set builtin to opt into fallback on transport failure
|
||
|
||
# =============================================================================
|
||
# Browser Tool Configuration
|
||
# =============================================================================
|
||
browser:
|
||
# Inactivity timeout in seconds - browser sessions are automatically closed
|
||
# after this period of no activity between agent loops (default: 120 = 2 minutes)
|
||
inactivity_timeout: 120
|
||
# Let an authenticated browser extension register as the controller for an
|
||
# existing Hermes session. Disabled by default. Local API registration also
|
||
# requires the API server bearer key to be configured.
|
||
extension_control:
|
||
enabled: false
|
||
|
||
# Maximum characters of snapshot content before truncate-and-store.
|
||
# Increase for long pages (for example, documentation or financial reports),
|
||
# or decrease to reduce context usage. Minimum: 1000; default: 15000
|
||
# (same per-page budget as web_extract).
|
||
# snapshot_threshold: 15000
|
||
|
||
# =============================================================================
|
||
# Tool Loop Guardrails
|
||
# =============================================================================
|
||
# Soft warnings are enabled by default. They append guidance to repeated failed
|
||
# or non-progressing tool results but still let the tool execute. Hard stops stay
|
||
# opt-in for interactive CLI/TUI/Desktop/ACP sessions, but default on for unattended
|
||
# gateway/cron sessions where nobody is present to interrupt a model that
|
||
# ignores loop warnings.
|
||
tool_loop_guardrails:
|
||
warnings_enabled: true
|
||
hard_stop_enabled: false
|
||
non_interactive_hard_stop_enabled: true
|
||
warn_after:
|
||
exact_failure: 2
|
||
same_tool_failure: 3
|
||
idempotent_no_progress: 2
|
||
hard_stop_after:
|
||
exact_failure: 5
|
||
same_tool_failure: 8
|
||
idempotent_no_progress: 5
|
||
|
||
# =============================================================================
|
||
# Context Compression (Auto-shrinks long conversations)
|
||
# =============================================================================
|
||
# When conversation approaches model's context limit, middle turns are
|
||
# automatically summarized to free up space while preserving important context.
|
||
#
|
||
# HOW IT WORKS:
|
||
# 1. Tracks actual token usage from API responses (not estimates)
|
||
# 2. When prompt_tokens >= threshold% of model's context_length, triggers compression
|
||
# 3. Protects first 3 turns (system prompt, initial request, first response)
|
||
# 4. Protects last N turns (default 20 messages = ~10 full turns of recent context)
|
||
# 5. Summarizes middle turns using a fast/cheap model
|
||
# 6. Inserts summary as a user message, continues conversation seamlessly
|
||
#
|
||
# Post-compression tail budget is target_ratio × threshold × context_length:
|
||
# 200K context, threshold 0.50, ratio 0.20 → 20K tokens of recent tail preserved
|
||
# 1M context, threshold 0.50, ratio 0.20 → 100K tokens of recent tail preserved
|
||
#
|
||
compression:
|
||
# Enable automatic context compression (default: true)
|
||
# Set to false if you prefer to manage context manually or want errors on overflow
|
||
enabled: true
|
||
|
||
# Fail closed before lossy compaction unless an active memory provider that
|
||
# implements the pre-compress checkpoint contract (API v2) confirms its
|
||
# durable checkpoint (default: false). With this on and no checkpoint, the
|
||
# compaction attempt errors with BLOCKED_MISSING_PREREQUISITE and the
|
||
# uncompressed transcript is preserved for a later retry. The gate binds to
|
||
# every compaction authority: server-side native compaction
|
||
# (codex_responses_native) is suppressed while armed, post-turn
|
||
# micro-compaction is forced off (no checkpoint hook in its path), and the
|
||
# codex_app_server API mode is refused at agent init (the codex agent
|
||
# compacts its own thread — no checkpoint can be guaranteed there). Only
|
||
# enable it with a checkpoint-capable provider configured — see
|
||
# website/docs/developer-guide/memory-provider-plugin.md
|
||
# ("Pre-Compress Checkpoints").
|
||
checkpoint_required: false
|
||
|
||
# Opt-in compression progress notices on chat platforms (default: false).
|
||
# By design, routine automatic compression is SILENT on human-facing chat
|
||
# gateways (Telegram, Discord, Slack, ...) — it happens in the background
|
||
# with server-side logging only. Set true to also deliver the routine
|
||
# progress statuses (compacting started, preflight/pre-API compression,
|
||
# idle compaction, retry progress, and the compaction-complete notice) to
|
||
# chat platforms. Unrelated operational noise (auxiliary model failures,
|
||
# provider retry chatter) stays suppressed either way, and compression
|
||
# FAILURE notices + manual /compress feedback are always visible
|
||
# regardless of this setting. (#52995)
|
||
progress_notices: false
|
||
|
||
# Trigger compression at this % of model's context limit (default: 0.50 = 50%)
|
||
# Lower values = more aggressive compression, higher values = compress later
|
||
# Models with context windows below 512K are floored at 0.75 (raise-only) so
|
||
# compaction doesn't fire with half the window still free; set above 0.75 to override.
|
||
threshold: 0.50
|
||
|
||
# Per-model threshold overrides: keys are substring-matched against the model
|
||
# name (longest match wins). Useful when some models need different compaction
|
||
# points — e.g. a 1M-context model can compress later (0.30) while a 128K
|
||
# model needs to compress earlier (0.60). The small-context floor (75% for
|
||
# <512K models) still applies on top of per-model overrides.
|
||
# model_thresholds:
|
||
# "glm-5.2": 0.40
|
||
# "claude-sonnet": 0.35
|
||
# "gpt-5": 0.30
|
||
|
||
# Optional absolute token cap for the compression trigger (default: null = disabled).
|
||
# When set, compression fires at the LOWER of the ratio-based threshold and this
|
||
# absolute token count — first-fires-wins. It never fires later than this count
|
||
# regardless of which model is active (useful when switching between models with
|
||
# very different context windows). Clamped to the model's context length at
|
||
# apply-time, so a cap above the window is a no-op (ratio-based threshold wins).
|
||
# Survives model switches and fallback activations.
|
||
# threshold_tokens: 200000
|
||
|
||
# Existing Codex gpt-5.5 behavior: raise Hermes' compaction trigger to 85%
|
||
# for the ChatGPT Codex OAuth route. Set false to opt back down to threshold.
|
||
codex_gpt55_autoraise: true
|
||
|
||
# Fraction of the threshold to preserve as recent tail (default: 0.20 = 20%)
|
||
# e.g. 20% of 50% threshold = 10% of total context kept as recent messages.
|
||
# Summary output is separately capped at 12K tokens (Gemini output limit).
|
||
# Range: 0.10 - 0.80
|
||
target_ratio: 0.20
|
||
|
||
# Number of most-recent messages to always preserve (default: 20 ≈ 10 full turns)
|
||
# Higher values keep more recent conversation intact at the cost of more aggressive
|
||
# compression of older turns.
|
||
protect_last_n: 20
|
||
|
||
# Minimum number of REAL (actionable) user messages guaranteed to survive in
|
||
# the uncompressed tail (default: 1 = the existing single last-user anchor,
|
||
# behavior-preserving). Raise to e.g. 3 to keep the last 3 real user turns
|
||
# verbatim even when bulky tool outputs fill the tail token budget — blank
|
||
# platform echoes, compaction handoffs, and synthetic continuation rows never
|
||
# count toward N. The tail can exceed the token budget when this pulls the
|
||
# cut back; the guarantee wins over the budget by design.
|
||
min_tail_user_messages: 1
|
||
|
||
# Compression retry rounds before a turn gives up with "max compression
|
||
# attempts reached" (default: 3, same as the previous hardcoded value).
|
||
# Raise (e.g. 6) for tool-schema-heavy sessions where 3 rounds cannot bring
|
||
# the request estimate under the threshold. Validated >= 1, hard cap 10.
|
||
max_attempts: 3
|
||
|
||
# Codex app-server (codex CLI runtime) thread-compaction mode. The codex
|
||
# agent owns the real thread context on this runtime, so Hermes' summarizer
|
||
# cannot shrink it — compaction goes through the app server instead.
|
||
# native = let Codex decide when to compact its own thread (default)
|
||
# hermes = let Hermes threshold trigger Codex thread/compact/start
|
||
# off = Hermes will not auto-trigger compaction; Codex may still compact natively
|
||
codex_app_server_auto: native
|
||
|
||
# Native OpenAI Responses server-side compaction (default: false). When true,
|
||
# gpt-5.6-family models on the DIRECT OpenAI API (api.openai.com) or a ChatGPT
|
||
# Codex subscription compact server-side: OpenAI prunes older context into an
|
||
# encrypted checkpoint that Hermes replays on later turns. No other provider,
|
||
# route, or model is affected. Hermes' local compression stays armed as the
|
||
# fallback and still handles every non-eligible session.
|
||
codex_responses_native: false
|
||
|
||
# Optional absolute server compaction trigger in input tokens. The default
|
||
# null value follows the resolved local compression trigger with an 8192 token
|
||
# safety margin. For example, a local trigger of 765000 selects 756808.
|
||
# A positive integer stays absolute and only clamps downward when needed so
|
||
# the server compacts first. Invalid values use this automatic behavior. If
|
||
# the local trigger is unavailable, automatic mode uses 200000.
|
||
codex_responses_compact_threshold: null
|
||
|
||
# Number of non-system messages to protect at the head of the transcript, in
|
||
# ADDITION to the system prompt (which is always implicitly protected).
|
||
# Head messages are NEVER summarized — they survive every compression
|
||
# indefinitely. This gives stable early context for short/medium sessions,
|
||
# but in long-running sessions that rely on rolling compaction the pinned
|
||
# opening turns may not match how you want the session framed over time.
|
||
# Set to 0 to preserve ONLY the system prompt (plus the rolling summary
|
||
# and recent tail) — the cleanest configuration for long-running sessions.
|
||
# Default 3 preserves the system prompt plus the first three non-system
|
||
# head messages, matching the pre-feature behaviour.
|
||
protect_first_n: 3
|
||
|
||
# Idle compaction (default: 0 = disabled). When > 0, a session that resumes
|
||
# after at least this many seconds of inactivity compacts its accumulated
|
||
# history up front, before the first reply, so a long-lived thread you come
|
||
# back to later doesn't re-read its full stale context on every turn.
|
||
# Time-based, so it complements (does not replace) the size-based `threshold`
|
||
# above. It is skipped when the context is already small (at or below the
|
||
# post-compression target = threshold × target_ratio), so it never wastes a
|
||
# summarization on a short idle thread. Example: 1800 = compact after 30 min idle.
|
||
idle_compact_after_seconds: 0
|
||
|
||
# Gateway session-hygiene turn-hold budget (default: 10). Max seconds an
|
||
# arriving user turn is held while a still-streaming hygiene summary
|
||
# finishes. Distinct from hygiene_timeout_seconds (compressor inactivity
|
||
# budget): this bounds user-visible latency so chat transports (Telegram
|
||
# ~30s) do not drop a silent connection. On expiry the turn proceeds
|
||
# uncompressed; the detached worker keeps its commit admission (when the
|
||
# commit is watermark-fenced) and the summary is adopted at the next safe
|
||
# boundary. Thinking-model summarizers often need longer than 10s to emit
|
||
# the first content token — raise to 300 (or >= your summarizer's real
|
||
# time-to-first-content) only if you want THIS turn to wait for the
|
||
# compression instead of adopting it one turn late.
|
||
hygiene_max_turn_hold_seconds: 10
|
||
|
||
# Proactive tool-result prune (default: 0 = disabled). Opt-in token trigger
|
||
# for a deterministic, no-LLM prune of OLD tool-result payloads, run
|
||
# independently of `threshold` above. On large-window models (512K/1M) the
|
||
# ratio threshold rarely fires, so bulky tool outputs (terminal dumps, file
|
||
# reads, web extracts) ride along in history and get re-billed every turn.
|
||
# When re-sent history exceeds this many tokens, the prune dedupes identical
|
||
# results, summarizes older oversized ones, and truncates large tool-call
|
||
# arguments — protecting the most recent `protect_last_n` messages and never
|
||
# calling the model. Try 48000 to enable. Built-in compressor engine only;
|
||
# other context engines inherit a safe no-op.
|
||
# NOTE: a committed prune rewrites already-sent history, which invalidates
|
||
# the provider's prompt-cache prefix — the min_reclaim gate below keeps
|
||
# those cache breaks episodic (like a compression boundary) instead of
|
||
# per-turn.
|
||
proactive_prune_tokens: 0
|
||
|
||
# The prune's summarize pass only touches tool results larger than this many
|
||
# characters (clamped to >= 200 so a generated summary can't be
|
||
# re-summarized). Default 8000.
|
||
proactive_prune_min_result_chars: 8000
|
||
|
||
# A proactive prune only COMMITS when it reclaims at least this many tokens
|
||
# (measured on the pruned output). This is the prompt-cache hysteresis gate:
|
||
# one meaningful, amortized cache break per batch of stale tool output
|
||
# instead of a tiny break on every tool iteration. 0 = commit any non-zero
|
||
# prune. Default 4096.
|
||
proactive_prune_min_reclaim_tokens: 4096
|
||
|
||
# To pin a specific model/provider for compression summaries, use the
|
||
# auxiliary section below (auxiliary.compression.provider / model).
|
||
|
||
# =============================================================================
|
||
# Tool-result budget (optional)
|
||
# =============================================================================
|
||
# Controls when a large tool result is spilled to disk (full output saved to
|
||
# $HERMES_HOME/cache/spillover, preview + path kept in context). MCP tool
|
||
# results (tools named mcp_*) spill at a tighter 50,000-char threshold than
|
||
# the generic 100K default: MCP servers routinely return un-paginated 20-50K
|
||
# payloads that bloat context and slow every subsequent turn. Nothing is
|
||
# lost — the full result is on disk and readable with read_file.
|
||
#
|
||
# tool_budget:
|
||
# mcp_result_size_chars: 50000 # per-result spillover threshold for mcp_* tools
|
||
|
||
# =============================================================================
|
||
# Anthropic prompt caching TTL
|
||
# =============================================================================
|
||
# When prompt caching is active (Claude via OpenRouter or native Anthropic),
|
||
# Anthropic supports two TTL tiers for cached prefixes: "5m" (default) and
|
||
# "1h". Other values are ignored and "5m" is used.
|
||
#
|
||
prompt_caching:
|
||
cache_ttl: "5m" # use "1h" for long sessions with pauses between turns
|
||
|
||
# =============================================================================
|
||
# Auxiliary Models (Advanced — Experimental)
|
||
# =============================================================================
|
||
# Hermes uses lightweight "auxiliary" models for side tasks: image analysis,
|
||
# browser screenshot analysis, web page summarization, TTS audio-tag insertion,
|
||
# and context compression.
|
||
#
|
||
# By default these use Gemini Flash via OpenRouter or Nous Portal and are
|
||
# auto-detected from your credentials. You do NOT need to change anything
|
||
# here for normal usage.
|
||
#
|
||
# WARNING: Overriding these with providers other than OpenRouter or Nous Portal
|
||
# is EXPERIMENTAL and may not work. Not all models/providers support vision,
|
||
# produce usable summaries, or accept the same API format. Change at your own
|
||
# risk — if things break, reset to "auto" / empty values.
|
||
#
|
||
# Each task has its own provider + model pair so you can mix providers.
|
||
# For example: OpenRouter for vision (needs multimodal), but your main
|
||
# local endpoint for compression (just needs text).
|
||
#
|
||
# Provider options:
|
||
# "auto" - Best available: OpenRouter → Nous Portal → main endpoint (default)
|
||
# "openrouter" - Force OpenRouter (requires OPENROUTER_API_KEY)
|
||
# "nous" - Force Nous Portal (requires: hermes auth add nous)
|
||
# "gemini" - Force Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)
|
||
# "ollama-cloud" - Ollama Cloud (requires: OLLAMA_API_KEY)
|
||
# "codex" - Force Codex OAuth (requires: hermes model → Codex).
|
||
# Uses gpt-5.3-codex which supports vision.
|
||
# "main" - Use your custom endpoint (OPENAI_BASE_URL + OPENAI_API_KEY).
|
||
# Works with OpenAI API, local models, or any OpenAI-compatible
|
||
# endpoint. Also falls back to Codex OAuth and API-key providers.
|
||
#
|
||
# Model: leave empty to use the provider's default. When empty, OpenRouter
|
||
# uses "google/gemini-3-flash-preview" and Nous uses "gemini-3-flash".
|
||
# Other providers pick a sensible default automatically.
|
||
#
|
||
# auxiliary:
|
||
# # Image analysis: vision_analyze tool + browser screenshots
|
||
# vision:
|
||
# provider: "auto"
|
||
# model: "" # e.g. "google/gemini-2.5-flash", "openai/gpt-4o"
|
||
# timeout: 30 # LLM API call timeout (seconds)
|
||
# download_timeout: 30 # Image HTTP download timeout (seconds)
|
||
# # Increase for slow connections or self-hosted image servers
|
||
# reasoning_effort: "" # Per-task thinking level: none, minimal, low, medium,
|
||
# # high, xhigh, max, ultra. Empty = provider default.
|
||
# # Works on every auxiliary task block (vision,
|
||
# # web_extract, compression, title_generation, curator,
|
||
# # background_review, moa_reference, ...). Example: run
|
||
# # compression at "low" and vision at "none" to cut
|
||
# # side-task latency/cost on reasoning models.
|
||
#
|
||
# # Web page scraping / summarization + browser page text extraction
|
||
# web_extract:
|
||
# provider: "auto"
|
||
# model: ""
|
||
# reasoning_effort: "low"
|
||
#
|
||
# # Gemini 3.1 TTS hidden audio-tag insertion
|
||
# tts_audio_tags:
|
||
# provider: "auto" # empty model = your main chat model
|
||
# model: ""
|
||
# timeout: 30
|
||
#
|
||
# # Automatic session title generation after the first exchange
|
||
# title_generation:
|
||
# enabled: true # set false to disable auto-title generation
|
||
# provider: "auto"
|
||
# model: ""
|
||
# timeout: 30
|
||
# language: "" # empty = match the user's language; or e.g. "English"
|
||
#
|
||
# # Session search — summarizes matching past sessions
|
||
# session_search:
|
||
# provider: "auto"
|
||
# model: ""
|
||
# timeout: 30
|
||
# max_concurrency: 3 # Limit parallel summaries to reduce request-burst 429s
|
||
# extra_body: {} # Provider-specific OpenAI-compatible request fields
|
||
# # Example for providers that support request-body
|
||
# # reasoning controls:
|
||
# # extra_body:
|
||
# # enable_thinking: false
|
||
# # Some vLLM/Qwen deployments expect this nested:
|
||
# # extra_body:
|
||
# # chat_template_kwargs:
|
||
# # enable_thinking: false
|
||
#
|
||
# # Auto-generated short session titles after the first exchange.
|
||
# # Each active Discord/Telegram channel can spawn a background title
|
||
# # call. Cap concurrency to keep retries during provider incidents
|
||
# # from amplifying the request burst. Leave unset for legacy behavior
|
||
# # (unlimited).
|
||
# title_generation:
|
||
# provider: "auto"
|
||
# model: ""
|
||
# # max_concurrency: 2 # Optional: cap simultaneous title calls
|
||
#
|
||
# # Context compression — summarizes long sessions to shrink the prompt.
|
||
# # Heavy and often hits the slowest provider chain. Setting a small
|
||
# # cap prevents many sessions from compressing simultaneously during
|
||
# # provider degradation. Leave unset for legacy behavior (unlimited).
|
||
# compression:
|
||
# provider: "auto"
|
||
# model: ""
|
||
# # max_concurrency: 2 # Optional: cap simultaneous compression calls
|
||
#
|
||
# # Post-turn memory/skill self-improvement review fork. Runs after a turn
|
||
# # when the nudge intervals fire; writes skills/memories in a daemon thread.
|
||
# # Usage is recorded under session_model_usage task='background_review'.
|
||
# background_review:
|
||
# enabled: true # false = skip automatic forks (/refine still works)
|
||
# provider: "auto" # or pin a cheaper model (see memory.md)
|
||
# model: ""
|
||
|
||
# =============================================================================
|
||
# Persistent Memory
|
||
# =============================================================================
|
||
# Bounded curated memory injected into the system prompt every session.
|
||
# Two stores: MEMORY.md (agent's notes) and USER.md (user profile).
|
||
# Character limits keep the memory small and focused. The agent manages
|
||
# pruning -- when at the limit, it must consolidate or replace entries.
|
||
# Disabled by default in batch_runner.
|
||
#
|
||
memory:
|
||
# Agent's personal notes: environment facts, conventions, things learned
|
||
memory_enabled: true
|
||
|
||
# User profile: preferences, communication style, expectations
|
||
user_profile_enabled: true
|
||
|
||
# Character limits (~2.75 chars per token, model-independent)
|
||
memory_char_limit: 2200 # ~800 tokens
|
||
user_char_limit: 1375 # ~500 tokens
|
||
|
||
# Periodic memory nudge: remind the agent to consider saving memories
|
||
# every N user turns. Set to 0 to disable. Only active when memory is enabled.
|
||
nudge_interval: 10 # Nudge every 10 user turns (0 = disabled)
|
||
|
||
# =============================================================================
|
||
# Session Reset Policy (Messaging Platforms)
|
||
# =============================================================================
|
||
# Controls when messaging sessions (Telegram, Discord, WhatsApp, Slack) are
|
||
# automatically cleared. Default is "none": sessions never auto-reset —
|
||
# conversation context lives until you /reset or /new manually, or context
|
||
# compression kicks in. Opt in to automatic resets if you prefer sessions to
|
||
# clear on a schedule (long-lived context increases API cost per message,
|
||
# though prompt caching and compression keep this manageable).
|
||
#
|
||
# When an automatic reset triggers, the agent first saves important
|
||
# information to its persistent memory — but the conversation context is
|
||
# wiped. The agent starts fresh but retains learned facts via its memory
|
||
# system.
|
||
#
|
||
# Modes:
|
||
# "none" - Never auto-reset (default); context lives until /reset or compression
|
||
# "idle" - Reset after N minutes of inactivity
|
||
# "daily" - Reset at a fixed hour each day
|
||
# "both" - Reset on EITHER inactivity timeout or daily boundary
|
||
#
|
||
session_reset:
|
||
mode: none # "none", "idle", "daily", or "both"
|
||
idle_minutes: 1440 # Inactivity timeout in minutes (used by "idle"/"both")
|
||
at_hour: 4 # Daily reset hour, 0-23 local time (used by "daily"/"both")
|
||
|
||
# Maximum number of simultaneously active chat sessions across CLI, TUI,
|
||
# dashboard chat, and messaging gateway. Set to null, 0, or omit to allow
|
||
# unlimited concurrent sessions. When the limit is reached, new sessions get a
|
||
# clean error while existing active sessions keep their normal behavior. This
|
||
# top-level key takes precedence over gateway.max_concurrent_sessions. The cap
|
||
# is a best-effort single-host/profile runtime guard; Hermes fails open if the
|
||
# local runtime lease registry cannot be read or locked.
|
||
max_concurrent_sessions: null
|
||
|
||
# When true, group/channel chats use one session per participant when the platform
|
||
# provides a user ID. This is the secure default and prevents users in the same
|
||
# room from sharing context, interrupts, and token costs. Set false only if you
|
||
# explicitly want one shared "room brain" per group/channel.
|
||
group_sessions_per_user: true
|
||
|
||
# =============================================================================
|
||
# Session Storage Retention (state.db)
|
||
# =============================================================================
|
||
# ~/.hermes/state.db keeps every session, message, and tool call, plus the
|
||
# FTS5 search indexes. Since #54189, auto-pruning is ON by default so the file
|
||
# stays bounded: at CLI/gateway/cron startup (at most once per
|
||
# min_interval_hours) Hermes deletes ENDED sessions whose last activity is
|
||
# older than retention_days. Open, pinned, and in-progress sessions are never
|
||
# deleted. Stale automation sessions (cron/kanban/subagent/one-shot CLI) whose
|
||
# process died without closing them are first *closed*, then aged through a
|
||
# further full retention window before removal.
|
||
#
|
||
# After a prune that removed rows, VACUUM reclaims disk space only when both
|
||
# the time throttle (min_vacuum_interval_days) has elapsed AND more than 25%
|
||
# of the file's pages are reclaimable — a dense database never pays for a full
|
||
# rewrite to reclaim a few MB.
|
||
#
|
||
# Uncomment to change the defaults shown; set auto_prune: false to keep every
|
||
# ended session forever (the pre-#54189 behavior).
|
||
# sessions:
|
||
# auto_prune: true
|
||
# retention_days: 90
|
||
# vacuum_after_prune: true
|
||
# min_vacuum_interval_days: 30
|
||
# min_interval_hours: 24
|
||
|
||
# Optional direct endpoint for autonomous Bot Mode rooms spanning gateways.
|
||
# Leave unset for the safe default: Desktop coordinates cross-gateway rooms and
|
||
# same-gateway rooms can still continue on their own. Set this only to the
|
||
# public HTTPS base URL that another trusted Hermes gateway can reach. The API
|
||
# server key authorizes the initial invitation; scoped grants are signed by a
|
||
# separate installation-private secret and survive API-key rotation. Disbanding
|
||
# a Group Chat revokes its known peer routes; API-key rotation alone does not.
|
||
# Network access should still be limited to the peer gateways (for example with
|
||
# your VPN or firewall).
|
||
# Plain HTTP is accepted only for loopback testing; every reachable peer URL
|
||
# must use HTTPS.
|
||
#
|
||
# gateway:
|
||
# room_link_url: "https://hermes.example.net"
|
||
#
|
||
# The equivalent environment override is HERMES_ROOM_LINK_URL. After changing
|
||
# it, restart the gateway and reopen New Group Chat > Advanced; Hermes will
|
||
# re-check the selected gateways automatically.
|
||
|
||
# Startup sweep of session rows orphaned by a dead gateway process.
|
||
# The normal disconnect cleanup runs on an in-process grace timer, so a
|
||
# gateway restart (update, crash, systemd) leaves those rows permanently
|
||
# "active". On every gateway boot — stdio TUI *and* the desktop/dashboard
|
||
# WS sidecar — tui/desktop/subagent rows whose start time AND newest
|
||
# message are both older than the session TTL (HERMES_TUI_SESSION_TTL_S,
|
||
# default 6h) are closed with end_reason "startup_orphan_reap".
|
||
# Messaging-platform sessions (Telegram, Discord, ...) are never touched;
|
||
# live in-memory sessions are excluded; swept sessions stay resumable.
|
||
#
|
||
# dashboard:
|
||
# startup_orphan_sweep: true
|
||
|
||
# ─────────────────────────────────────────────────────────────────────────────
|
||
# API Server — per-client model routing
|
||
# ─────────────────────────────────────────────────────────────────────────────
|
||
# Route different API clients to different models/providers on a single
|
||
# Hermes deployment. Clients choose a backend by sending a specific string
|
||
# as the OpenAI ``model`` field. Unmapped model values fall back to the
|
||
# global model configured in the ``model:`` section above, and an explicit
|
||
# session /model override always wins over a route.
|
||
#
|
||
# Configure via the ``platforms.api_server.extra.model_routes`` gateway
|
||
# config block:
|
||
#
|
||
# platforms:
|
||
# api_server:
|
||
# enabled: true
|
||
# extra:
|
||
# key: "your-api-server-secret"
|
||
# model_routes:
|
||
# # Xiaozhi clients send model="minimax-m2" → routed to MiniMax via OpenRouter
|
||
# minimax-m2:
|
||
# model: "minimax/minimax-m1"
|
||
# provider: "openrouter" # optional — overrides global provider
|
||
# # api_key: "sk-..." # optional — per-route UPSTREAM provider
|
||
# # key (NOT caller auth; never logged)
|
||
# # base_url: "https://..." # optional — per-route base URL
|
||
# # GPT clients keep their own alias
|
||
# gpt-5:
|
||
# model: "openai/gpt-5"
|
||
# provider: "openrouter"
|
||
#
|
||
# Configured aliases are automatically listed by GET /v1/models so clients
|
||
# can discover them without manual coordination. Caller authentication is
|
||
# unchanged: every request still authenticates with the global API server
|
||
# key (``extra.key`` / API_SERVER_KEY).
|
||
|
||
# ─────────────────────────────────────────────────────────────────────────────
|
||
# Gateway Streaming
|
||
# ─────────────────────────────────────────────────────────────────────────────
|
||
# Stream tokens to messaging platforms in real-time. The bot sends a message
|
||
# on first token, then progressively edits it as more tokens arrive.
|
||
# Disabled by default — enable to try the streaming UX on Telegram/Discord/Slack.
|
||
# For Telegram, partial edits are sent as plain text and only the final edit uses MarkdownV2.
|
||
streaming:
|
||
enabled: false
|
||
# transport: edit # "edit" = progressive editMessageText
|
||
# edit_interval: 0.3 # seconds between message edits
|
||
# buffer_threshold: 40 # chars before forcing an edit flush
|
||
# cursor: " ▉" # cursor shown during streaming
|
||
|
||
# =============================================================================
|
||
# Skills Configuration
|
||
# =============================================================================
|
||
# Skills are reusable procedures the agent can load and follow. The agent can
|
||
# also create new skills after completing complex tasks.
|
||
#
|
||
skills:
|
||
# Nudge the agent to create skills after complex tasks.
|
||
# Every N tool-calling iterations, remind the model to consider saving a skill.
|
||
# Set to 0 to disable.
|
||
creation_nudge_interval: 15
|
||
|
||
# External skill directories — share skills across tools/agents without
|
||
# copying them into ~/.hermes/skills/. Each path is expanded (~ and ${VAR})
|
||
# and resolved to an absolute path. External dirs are read-only: skill
|
||
# creation always writes to ~/.hermes/skills/. Local skills take precedence
|
||
# when names collide.
|
||
# external_dirs:
|
||
# - ~/.agents/skills
|
||
# - /home/shared/team-skills
|
||
|
||
# =============================================================================
|
||
# Agent Behavior
|
||
# =============================================================================
|
||
agent:
|
||
# Maximum tool-calling iterations per conversation (default: 500)
|
||
# Higher = more room for complex tasks, but costs more tokens
|
||
# Recommended: 20-30 for focused tasks, 50-100 for open exploration
|
||
max_turns: 500
|
||
|
||
# Inactivity timeout for gateway agent runs (seconds, 0 = unlimited).
|
||
# The agent can run indefinitely when actively calling tools or receiving
|
||
# API responses. Only fires after the agent has been idle for this duration.
|
||
# gateway_timeout: 1800
|
||
|
||
# Maximum time an alias routing key waits for an active turn holding the same
|
||
# resolved session lease. On expiry Hermes rejects this inbound message and
|
||
# asks the user to resend rather than running it without serialization.
|
||
# Non-positive values fall back to the 5-second default.
|
||
# gateway_turn_lease_timeout: 5
|
||
|
||
# Staged warning: send a warning before escalating to full timeout.
|
||
# Fires once per run when inactivity reaches this threshold (seconds).
|
||
# Set to 0 to disable the warning.
|
||
# gateway_timeout_warning: 900
|
||
|
||
# Session stall watchdog (seconds). When a busy session has a pending
|
||
# inbound follow-up and the agent activity clock is idle this long, the
|
||
# gateway logs a WARNING and notifies the user to try /new. Does not kill
|
||
# the turn (see gateway_timeout). 0 = disable. Default 300.
|
||
# session_stall_timeout: 300
|
||
|
||
# Transcript-sanitiser repeated-heal escalation (#96870). The pre-send
|
||
# sanitiser silently repairs empty non-final turns on the wire copy; after
|
||
# this many heal passes within a 10-minute session window Hermes logs one
|
||
# ERROR (with session id + heal pattern) and sends a ONE-TIME out-of-band
|
||
# notice to the user suggesting /debug share or `hermes doctor`. The notice
|
||
# goes through the status channel only — conversation context and prompt
|
||
# caching are untouched. Set 0 to disable escalation (per-window WARNINGs
|
||
# still fire). Default 3.
|
||
# sanitizer_heal_escalation_threshold: 3
|
||
|
||
# Related in-agent compression timeouts (they live under the top-level
|
||
# compression: block, shown here for discoverability next to the stall
|
||
# watchdog they complement — a hung compression is a common stall cause):
|
||
# compression:
|
||
# context_timeout_seconds: 120 # inactivity budget for in-agent
|
||
# # compress_context (0 = disable)
|
||
# context_total_ceiling_seconds: 600 # absolute cap on the pre-commit
|
||
# # wait even while tokens stream
|
||
|
||
# Graceful drain timeout for gateway stop/restart (seconds).
|
||
# Default 0 = no drain: a restart interrupts in-flight agents immediately,
|
||
# cleans up, and exits. Set a positive value only if you want a grace
|
||
# window on /restart, and keep it well under systemd's TimeoutStopSec.
|
||
# restart_drain_timeout: 0
|
||
|
||
# Cron-only floor under the same drain (seconds). Default 30.
|
||
# restart_drain_timeout above is written for chat turns, which are cheap to
|
||
# interrupt: the user is told the gateway is restarting and the session
|
||
# resumes on their next message. A cron run has no such safety net — it is
|
||
# recorded in jobs.json as a permanent failure, nobody is waiting on it, and
|
||
# a recurring job simply skips to its next schedule. So in-flight cron work
|
||
# gets its own grace window instead of inheriting the 0 above.
|
||
# Clamped at runtime to the shutdown-watchdog leash (restart_drain_timeout
|
||
# + 60s) minus teardown headroom, so values past ~50s need a matching
|
||
# TimeoutStopSec bump to take effect. Set 0 to opt out and drain cron on
|
||
# restart_drain_timeout like before.
|
||
# cron_drain_timeout: 30
|
||
|
||
# Upper bound (seconds) a submitted prompt waits for the deferred agent
|
||
# build (MCP discovery, model metadata, skills scan) before failing with a
|
||
# visible error. The wait is patient — the message is delivered as soon as
|
||
# the build completes, and a progress notice is shown past 30s — so this cap
|
||
# only fires on a genuinely hung build. Raise it for deployments with many
|
||
# slow or unreachable MCP servers. Default 600.
|
||
# build_wait_timeout: 600
|
||
|
||
# Max app-level retry attempts for API errors (connection drops, provider
|
||
# timeouts, 5xx, etc.) before the agent surfaces the failure. Lower this
|
||
# to 1 if you use fallback providers and want fast failover on flaky
|
||
# primaries (default 3). The OpenAI SDK does its own low-level retries
|
||
# underneath this wrapper — this is the Hermes-level loop.
|
||
# api_max_retries: 3
|
||
|
||
# After the agent edits code without fresh passing verification, nudge it to
|
||
# verify before finishing. The default "auto" enables it on interactive
|
||
# coding surfaces (CLI, TUI, desktop) and programmatic callers, and disables
|
||
# it on conversational messaging surfaces (Telegram, Discord, etc.) where the
|
||
# verification summary would reach a human as chat noise. Set true or false to
|
||
# force it on or off; the HERMES_VERIFY_ON_STOP env var (1/0) takes precedence.
|
||
# verify_on_stop: auto
|
||
|
||
# Standing operator instructions for the coding posture (when Hermes is in a
|
||
# code workspace). Appended to the coding brief as an extra system block, so
|
||
# you can pin project-wide workflow rules without editing the shipped brief.
|
||
# Accepts a string or a list of strings. Takes effect next session.
|
||
# coding_instructions:
|
||
# - "For UI work, don't run tsc/lint until I approve the look."
|
||
# - "Clean the diff before you commit and push."
|
||
|
||
# When verify-on-stop finds edited code without fresh verification evidence,
|
||
# append guidance for creative UI work (avoid broad tsc/lint/test before visual
|
||
# approval) and clean-diff expectations. Set false to keep that nudge terse.
|
||
# verify_guidance: true
|
||
|
||
# A `pre_verify` hook (plugin or shell, see Event Hooks docs) can keep the
|
||
# agent going one more turn to verify/clean before finishing. This caps how
|
||
# many times one turn may be nudged to continue, so a hook can't trap the loop.
|
||
# Default 3.
|
||
# max_verify_nudges: 3
|
||
|
||
# Enable verbose logging
|
||
verbose: false
|
||
|
||
# Reasoning effort level (OpenRouter and Nous Portal)
|
||
# Controls how much "thinking" the model does before responding.
|
||
# Options: "xhigh" (max), "high", "medium", "low", "minimal", "none" (disable)
|
||
reasoning_effort: "medium"
|
||
|
||
# Per-model reasoning effort overrides (optional dict)
|
||
# Key: any sensible model spelling works (exact, dots↔dashes interchangeable,
|
||
# provider prefix optional). First match wins.
|
||
# Value: reasoning effort level (same options as reasoning_effort)
|
||
# Override the global reasoning_effort for that specific model.
|
||
# NOTE: no `hermes config set` support for this key -- edit YAML directly.
|
||
# reasoning_overrides:
|
||
# "openrouter/anthropic/claude-opus-4.5": "xhigh"
|
||
# "openai/gpt-5": "low"
|
||
# "claude-opus-4.6": "high" # bare model name also works
|
||
# "deepseek/deepseek-v4-pro": "xhigh" # dots and dashes are interchangeable
|
||
reasoning_overrides: {}
|
||
|
||
# Fast mode (OpenAI Priority Processing / xAI Grok 4.6 / Anthropic Fast Mode
|
||
# on Opus 4.8+). Premium pricing; only sent to first-party endpoints.
|
||
# "" / "normal" - off (default)
|
||
# "fast" - every request
|
||
# "auto" - only the first fast_auto_seconds of every turn
|
||
# "cold" - that window on the first turn of a session only
|
||
# Also: /fast normal|fast|auto|cold [--global]
|
||
service_tier: ""
|
||
fast_auto_seconds: 60
|
||
|
||
# Custom personalities (use with /personality command).
|
||
# Built-ins (helpful, concise, technical, creative, teacher, kawaii, catgirl,
|
||
# pirate, shakespeare, surfer, noir, uwu, philosopher, hype) are always
|
||
# available on every surface — defined once in hermes_cli/personality.py.
|
||
# Entries here ADD new personalities or OVERRIDE a built-in by name.
|
||
# The active selection is stored in display.personality (never here, and
|
||
# never in agent.system_prompt — that field is your manual system prompt).
|
||
personalities: {}
|
||
# mentor: "You are a supportive mentor. Guide, don't lecture."
|
||
# reviewer:
|
||
# system_prompt: "You are a meticulous code reviewer."
|
||
# tone: "direct"
|
||
# style: "terse"
|
||
|
||
# =============================================================================
|
||
# Gateway Lifecycle
|
||
# =============================================================================
|
||
gateway:
|
||
# Post-interrupt grace for running agents during an unexpected SIGTERM.
|
||
# Adapter, bridge, and database teardown starts after this many seconds even
|
||
# if an agent has not unwound. Keep it below the service-manager stop budget.
|
||
# signal_interrupt_grace_timeout: 1
|
||
|
||
# Let platform adapters honor HTTP_PROXY / HTTPS_PROXY / NO_PROXY (and
|
||
# SSL_CERT_FILE) from the process environment, plus macOS system-proxy
|
||
# auto-detection. Set to false when the gateway inherits a proxy it must not
|
||
# use (e.g. a Windows Scheduled Task picking up a local Clash/V2Ray proxy that
|
||
# isn't running). Explicit per-platform vars like DISCORD_PROXY still apply.
|
||
# trust_env: true
|
||
|
||
# =============================================================================
|
||
# Toolsets
|
||
# =============================================================================
|
||
# Control which tools the agent has access to.
|
||
# Use `hermes tools` to interactively enable/disable tools per platform.
|
||
|
||
# =============================================================================
|
||
# Platform Toolsets (per-platform tool configuration)
|
||
# =============================================================================
|
||
# Override which toolsets are available on each platform.
|
||
# If a platform isn't listed here, its built-in default is used.
|
||
#
|
||
# You can use EITHER:
|
||
# - A preset like "hermes-cli" or "hermes-telegram" (curated tool set)
|
||
# - A list of individual toolsets to compose your own (see list below)
|
||
#
|
||
# Supported platform keys: cli, telegram, discord, whatsapp, slack, qqbot, teams, google_chat
|
||
#
|
||
# Examples:
|
||
#
|
||
# # Use presets (same as defaults):
|
||
# platform_toolsets:
|
||
# cli: [hermes-cli]
|
||
# telegram: [hermes-telegram]
|
||
#
|
||
# # Custom: give Telegram only web + terminal + file + planning:
|
||
# platform_toolsets:
|
||
# telegram: [web, terminal, file, todo]
|
||
#
|
||
# # Custom: CLI without browser or image gen:
|
||
# platform_toolsets:
|
||
# cli: [web, terminal, file, skills, todo, tts, cronjob]
|
||
#
|
||
# # Restrictive: Discord gets read-only tools only:
|
||
# platform_toolsets:
|
||
# discord: [web, vision, skills, todo]
|
||
#
|
||
# If not set, defaults are:
|
||
# cli: hermes-cli (everything + cronjob management)
|
||
# telegram: hermes-telegram (terminal, file, web, vision, image, tts, browser, skills, todo, cronjob, messaging)
|
||
# discord: hermes-discord (same as telegram)
|
||
# whatsapp: hermes-whatsapp (same as telegram)
|
||
# slack: hermes-slack (same as telegram)
|
||
# signal: hermes-signal (same as telegram)
|
||
# homeassistant: hermes-homeassistant (same as telegram)
|
||
# qqbot: hermes-qqbot (same as telegram)
|
||
# teams: hermes-teams (same as telegram)
|
||
# google_chat: hermes-google_chat (same as telegram)
|
||
#
|
||
platform_toolsets:
|
||
cli: [hermes-cli]
|
||
telegram: [hermes-telegram]
|
||
discord: [hermes-discord]
|
||
whatsapp: [hermes-whatsapp]
|
||
slack: [hermes-slack]
|
||
signal: [hermes-signal]
|
||
homeassistant: [hermes-homeassistant]
|
||
qqbot: [hermes-qqbot]
|
||
yuanbao: [hermes-yuanbao]
|
||
teams: [hermes-teams]
|
||
google_chat: [hermes-google_chat]
|
||
|
||
# =============================================================================
|
||
# Gateway Platform Settings
|
||
# =============================================================================
|
||
# Optional per-platform messaging settings.
|
||
# Platform-specific knobs live under `extra`.
|
||
#
|
||
# platforms:
|
||
# telegram:
|
||
# reply_to_mode: "first" # off | first | all
|
||
# # guest_mode lets explicit @mentions from non-allowlisted groups through.
|
||
# # Default false; ordinary messages, replies, and regex wake words stay blocked.
|
||
# guest_mode: false
|
||
# # allowed_chats: ["-1001234567890"]
|
||
# extra:
|
||
# disable_link_previews: false # Set true to suppress Telegram URL previews in bot messages
|
||
# rich_messages: false # Bot API 10.1 rich messages (tables/task lists/details/math); default false for copyable legacy MarkdownV2, set true to opt in
|
||
# rich_drafts: false # Experimental rich draft previews during Telegram DM streaming; default false because Telegram Desktop/macOS can visually overlay draft frames
|
||
# command_menu:
|
||
# # Telegram allows up to 100 BotCommands; Hermes defaults to 60 so
|
||
# # all built-in commands plus common skill commands stay visible
|
||
# # while remaining under Telegram's payload-size limit. Clamped 1..100.
|
||
# max_commands: 60
|
||
# # prepend = user priority first, then Hermes defaults
|
||
# # append = Hermes defaults first, then user priority
|
||
# # replace = only the list below defines priority
|
||
# priority_mode: prepend
|
||
# # Priority is applied across core + plugin + skill commands before
|
||
# # the cap, so a listed skill command always keeps a menu slot.
|
||
# priority:
|
||
# - my_plugin_command
|
||
# - my-important-skill
|
||
# slack:
|
||
# extra:
|
||
# # Render live tool calls as Slack-native plan/task cards. This explicit
|
||
# # opt-in works even though Slack text tool_progress defaults to off.
|
||
# native_task_cards: false
|
||
# # Slack user IDs whose Web-API posts (user token, e.g. your own
|
||
# # dashboard/mobile front-end) count as human instead of being dropped
|
||
# # as app traffic. Narrower than allow_bots: all. Users only — never apps.
|
||
# api_human_users: ["U0AAAAAAA", "U0BBBBBBB"]
|
||
# # Suppress automatic link-preview cards without removing clickable links.
|
||
# # Omit either key to preserve Slack's default for that preview type.
|
||
# unfurl_links: false
|
||
# unfurl_media: false
|
||
# webhook:
|
||
# extra:
|
||
# # Route scripts default to a 30 second timeout. Scripts must live under
|
||
# # the active profile's scripts directory and receive webhook JSON on stdin.
|
||
# script_timeout_seconds: 30
|
||
#
|
||
# Discord-specific settings (config.yaml top-level, not under platforms:):
|
||
#
|
||
# discord:
|
||
# require_mention: true # Require @mention in server channels (default: true)
|
||
# auto_thread: true # Auto-create thread on @mention (default: true)
|
||
# free_response_channels: "" # Channel IDs where no mention is needed
|
||
# reactions: true # Show processing reactions (default: true)
|
||
# history_backfill: true # Recover missed channel messages on mention (default: true)
|
||
# history_backfill_limit: 50 # Max messages to scan backwards (default: 50)
|
||
|
||
# ─────────────────────────────────────────────────────────────────────────────
|
||
# Available toolsets (use these names in platform_toolsets or the toolsets list)
|
||
#
|
||
# Run `hermes chat --list-toolsets` to see all toolsets and their tools.
|
||
# Run `hermes chat --list-tools` to see every individual tool with descriptions.
|
||
# ─────────────────────────────────────────────────────────────────────────────
|
||
#
|
||
# INDIVIDUAL TOOLSETS (compose your own):
|
||
# web - web_search, web_extract
|
||
# search - web_search only (no scraping)
|
||
# terminal - terminal, process
|
||
# file - read_file, write_file, patch, search
|
||
# browser - browser_navigate, browser_snapshot, browser_click, browser_type,
|
||
# browser_scroll, browser_back, browser_press,
|
||
# browser_get_images, browser_vision (requires BROWSERBASE_API_KEY)
|
||
# vision - vision_analyze (requires OPENROUTER_API_KEY)
|
||
# image_gen - image_generate (requires FAL_KEY)
|
||
# skills - skills_list, skill_view
|
||
# skills_hub - skill_hub (search/install/manage from online registries — user-driven only)
|
||
# todo - todo (in-memory task planning, no deps)
|
||
# tts - text_to_speech (Edge TTS free, or ELEVENLABS/OPENAI/MINIMAX/MISTRAL key)
|
||
# cronjob - cronjob (create/list/update/pause/resume/run/remove scheduled tasks)
|
||
#
|
||
# PRESETS (curated bundles):
|
||
# hermes-cli - All of the above except send_message
|
||
# hermes-telegram - terminal, file, web, vision, image_gen, tts, browser,
|
||
# skills, todo, cronjob, send_message
|
||
# hermes-discord - Same as hermes-telegram
|
||
# hermes-whatsapp - Same as hermes-telegram
|
||
# hermes-slack - Same as hermes-telegram
|
||
#
|
||
# COMPOSITE:
|
||
# debugging - terminal + web + file
|
||
# safe - web + vision (no terminal access)
|
||
# all - Everything available
|
||
#
|
||
# web - Web search and content extraction (web_search, web_extract)
|
||
# search - Web search only, no scraping (web_search)
|
||
# terminal - Command execution and process management (terminal, process)
|
||
# file - File operations: read, write, patch, search
|
||
# browser - Full browser automation (navigate, click, type, screenshot, etc.)
|
||
# vision - Image analysis (vision_analyze)
|
||
# image_gen - Image generation with FLUX (image_generate)
|
||
# skills - Load skill documents (skills_list, skill_view)
|
||
# todo - Task planning and tracking for multi-step work
|
||
# memory - Persistent memory across sessions (personal notes + user profile)
|
||
# session_search - Search and recall past conversations (FTS5 + Gemini Flash summarization)
|
||
# tts - Text-to-speech (Edge TTS free, ElevenLabs, OpenAI, MiniMax, Mistral)
|
||
# cronjob - Schedule and manage automated tasks (CLI-only)
|
||
#
|
||
# Composite toolsets:
|
||
# debugging - terminal + web + file (for troubleshooting)
|
||
# safe - web + vision (no terminal access)
|
||
|
||
# NOTE: The top-level "toolsets" key is deprecated and ignored.
|
||
# Tool configuration is managed per-platform via platform_toolsets above.
|
||
# Use `hermes tools` to configure interactively, or edit platform_toolsets directly.
|
||
#
|
||
# CLI override: hermes chat --toolsets terminal,web,file
|
||
|
||
# =============================================================================
|
||
# MCP (Model Context Protocol) Servers
|
||
# =============================================================================
|
||
# Connect to external MCP servers to add tools from the MCP ecosystem.
|
||
# Each server's tools are automatically discovered and registered.
|
||
# See website/docs/user-guide/features/mcp.md for full documentation.
|
||
#
|
||
# Stdio servers (spawn a subprocess):
|
||
# command: the executable to run
|
||
# args: command-line arguments
|
||
# env: environment variables (only these + safe defaults passed to subprocess)
|
||
#
|
||
# HTTP servers (connect to a URL):
|
||
# url: the MCP server endpoint
|
||
# headers: HTTP headers (e.g., for authentication)
|
||
#
|
||
# Optional per-server settings:
|
||
# timeout: tool call timeout in seconds (default: 120)
|
||
# connect_timeout: initial connection timeout (default: 60)
|
||
# keepalive_interval: liveness ping cadence in seconds (default: 180).
|
||
# Lower it below the server's session TTL for servers that expire idle
|
||
# sessions quickly (e.g. Unreal Engine editor MCP, ~15s), otherwise idle
|
||
# tool calls hit an expired session and pay a slow reconnect. Floored at 5s.
|
||
#
|
||
# mcp_servers:
|
||
# time:
|
||
# command: uvx
|
||
# args: ["mcp-server-time"]
|
||
# filesystem:
|
||
# command: npx
|
||
# args: ["-y", "@modelcontextprotocol/server-filesystem", "/home/user"]
|
||
# notion:
|
||
# url: https://mcp.notion.com/mcp
|
||
# github:
|
||
# command: npx
|
||
# args: ["-y", "@modelcontextprotocol/server-github"]
|
||
# env:
|
||
# GITHUB_PERSONAL_ACCESS_TOKEN: "ghp_..."
|
||
#
|
||
# Sampling (server-initiated LLM requests) — enabled by default.
|
||
# Per-server config under the 'sampling' key:
|
||
# analysis:
|
||
# command: npx
|
||
# args: ["-y", "analysis-server"]
|
||
# sampling:
|
||
# enabled: true # default: true
|
||
# model: "gemini-3-flash" # override model (optional)
|
||
# max_tokens_cap: 4096 # max tokens per request
|
||
# timeout: 30 # LLM call timeout (seconds)
|
||
# max_rpm: 10 # max requests per minute
|
||
# allowed_models: [] # model whitelist (empty = all)
|
||
# max_tool_rounds: 5 # tool loop limit (0 = disable)
|
||
# log_level: "info" # audit verbosity
|
||
|
||
# =============================================================================
|
||
# Text-to-Speech
|
||
# =============================================================================
|
||
# TTS defaults to Edge TTS unless changed in ~/.hermes/config.yaml.
|
||
# Gemini TTS supports persona/director prompt files, and Gemini 3.1 Flash TTS
|
||
# can use a hidden auxiliary rewrite pass to insert expressive square-bracket
|
||
# audio tags into the TTS script without showing tags in chat.
|
||
#
|
||
# tts:
|
||
# provider: "gemini"
|
||
# speed: 1.0 # global speed multiplier (provider-specific overrides this)
|
||
# gemini:
|
||
# model: "gemini-3.1-flash-tts-preview"
|
||
# voice: "Kore"
|
||
# audio_tags: false
|
||
# persona_prompt_file: "" # e.g. ~/.hermes/tts/radio-host.md
|
||
# xai:
|
||
# voice_id: "eve" # built-in or custom voice ID from xAI Console
|
||
# language: "en" # BCP-47 code ("en", "pt-BR") or "auto"
|
||
# speed: 1.0 # 0.7-1.5 playback speed
|
||
# auto_speech_tags: false # insert expressive audio tags via LLM rewrite
|
||
# text_normalization: false # normalize numbers/abbreviations/symbols
|
||
# optimize_streaming_latency: 0 # 0-2, trades quality for lower latency
|
||
# sample_rate: 24000 # 22050 / 24000 / 44100 / 48000
|
||
# bit_rate: 128000 # MP3 bitrate (codec=mp3 only)
|
||
|
||
# =============================================================================
|
||
# Voice Transcription (Speech-to-Text)
|
||
# =============================================================================
|
||
# Automatically transcribe voice messages on messaging platforms.
|
||
# Providers: local (free, faster-whisper) | groq (free tier) | openai (Whisper API) | mistral (Voxtral Transcribe)
|
||
# Set the corresponding API key in .env: GROQ_API_KEY, OPENAI_API_KEY, or MISTRAL_API_KEY.
|
||
stt:
|
||
enabled: true
|
||
# provider: "local" # auto-detected if omitted
|
||
# --- Cloud pre-upload silence trim (groq/openai/mistral/xai/elevenlabs/deepinfra) ---
|
||
# Local whisper gets Silero VAD; cloud endpoints otherwise receive raw audio —
|
||
# silence inflates upload time, per-audio-minute billing, and hallucination risk.
|
||
# Collapses pauses with ffmpeg client-side; clips under 12s skip the trim, and
|
||
# on any failure the original uploads untouched.
|
||
# cloud_trim_silence: true # set false to always upload the original audio
|
||
# cloud_trim_threshold_db: -40 # audio quieter than this counts as silence
|
||
# cloud_trim_keep_ms: 300 # how much of each pause survives (keeps natural pacing)
|
||
# Optional static transcription prompt: vocabulary/context hints for
|
||
# prompt-capable backends. Composition is deterministic: this config value
|
||
# is the base, then `pre_transcription` hooks run in registration order with
|
||
# last-writer-wins semantics per field.
|
||
#
|
||
# Privacy: the final prompt is sent to the configured STT provider alongside
|
||
# the audio. Do not include secrets or session context you would not send to
|
||
# that provider.
|
||
#
|
||
# Length / truncation contract: Whisper-family backends (local, openai,
|
||
# groq, deepinfra) only use the final ~224 prompt tokens, so Hermes
|
||
# truncates longer prompts client-side (keeping the tail) with a WARNING
|
||
# log — never an error. Other backends own their own validation.
|
||
#
|
||
# Provider behavior:
|
||
# local (faster-whisper) -> `initial_prompt`, forwarded unchanged
|
||
# openai / groq -> `prompt`, forwarded unchanged
|
||
# mistral -> `prompt`, forwarded unchanged
|
||
# deepinfra -> OpenAI-compatible `prompt`, unchanged
|
||
# local_command/xai/
|
||
# elevenlabs -> unsupported; DEBUG log, then continue
|
||
# plugin providers -> `prompt` in `**extra`; plugin owns limits
|
||
# prompt: "Hermes, Teknium, Nous Research, kanban"
|
||
local:
|
||
model: "base" # tiny | base | small | medium | large-v3 | turbo
|
||
# language: "" # auto-detect; set to "en", "es", "fr", etc. to force
|
||
# initial_prompt: "" # Optional faster-whisper prompt, e.g. bias Chinese output to simplified Chinese
|
||
# --- Anti-hallucination hardening (whisper decodes junk from silence without these) ---
|
||
# vad: true # Silero VAD filter (default on) — silence never reaches whisper.
|
||
# # Set false to restore raw behavior (e.g. transcribing music/ambient audio).
|
||
# vad_min_silence_ms: 500 # min silence (ms) that splits speech chunks when vad is on
|
||
# no_speech_prob_threshold: 0.6 # drop a segment only if no_speech_prob > this...
|
||
# logprob_threshold: -1.0 # ...AND avg_logprob < this (both must hit — quiet real speech survives)
|
||
# unload_after_idle_seconds: 0 # 0=never unload (default); e.g. 300 releases the model after 5min idle (frees VRAM on GPU; next voice message reloads it)
|
||
language: "en" # GLOBAL language hint for every STT provider (per-provider language wins). Set "" for auto-detect.
|
||
# groq:
|
||
# model: "whisper-large-v3-turbo"
|
||
# language: "" # blank = stt.language > HERMES_LOCAL_STT_LANGUAGE > auto-detect
|
||
openai:
|
||
model: "whisper-1" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe | gpt-transcribe
|
||
language: "" # auto-detect; set to "en", "es", "fr", etc. to force
|
||
# mistral:
|
||
# model: "voxtral-mini-latest" # voxtral-mini-latest | voxtral-mini-2602
|
||
# deepinfra:
|
||
# # Model id is discovered live from the DeepInfra catalog filtered
|
||
# # by the `stt` surface tag — leave `model` blank to take the first
|
||
# # live result. Pin only when you need a specific Whisper variant.
|
||
# model: ""
|
||
|
||
# Text-to-speech. Only the deepinfra block is documented here — the
|
||
# remaining providers (edge, openai, xai, minimax, mistral, gemini,
|
||
# elevenlabs, neutts, kittentts, piper) inherit sensible defaults from
|
||
# DEFAULT_CONFIG in hermes_cli/config.py.
|
||
# tts:
|
||
# provider: "deepinfra"
|
||
# deepinfra:
|
||
# # Model id is discovered live from the DeepInfra catalog filtered
|
||
# # by the `tts` surface tag — leave `model` blank to take the first
|
||
# # live result.
|
||
# model: ""
|
||
# voice: "default"
|
||
|
||
# "Hey Hermes" hands-free wake word (off by default; toggle with /wake).
|
||
# Full engine/provider options inherit from DEFAULT_CONFIG in
|
||
# hermes_cli/config_defaults.py — only capture placement is documented here.
|
||
# wake_word:
|
||
# enabled: false
|
||
# capture: "auto" # auto | local | client
|
||
# # auto: backend PortAudio mic when one exists; a remote
|
||
# # desktop on a mic-less (headless/VPS) backend
|
||
# # streams its own mic via the wake.feed RPC.
|
||
# # local: always the backend mic (wake_word.input_device).
|
||
# # client: always desktop-streamed PCM (detection stays
|
||
# # on the backend).
|
||
|
||
# Image generation. Each provider plugin reads its own ``image_gen.<name>``
|
||
# block; deepinfra discovers models live from
|
||
# api.deepinfra.com/v1/openai/models filtered by the ``image-gen`` tag —
|
||
# no model id is hardcoded, so retired models disappear automatically.
|
||
# image_gen:
|
||
# provider: "deepinfra"
|
||
# deepinfra:
|
||
# # Leave `model` blank for the first live `image-gen`-tagged result.
|
||
# model: ""
|
||
|
||
# =============================================================================
|
||
# Response Pacing (Messaging Platforms)
|
||
# =============================================================================
|
||
# Add human-like delays between message chunks.
|
||
# human_delay:
|
||
# mode: "off" # "off" | "natural" | "custom"
|
||
# min_ms: 800 # Min delay (custom mode only)
|
||
# max_ms: 2500 # Max delay (custom mode only)
|
||
|
||
# =============================================================================
|
||
# Session Logging
|
||
# =============================================================================
|
||
# Session trajectories are automatically saved to logs/ directory.
|
||
# Each session creates: logs/session_YYYYMMDD_HHMMSS_UUID.json
|
||
#
|
||
# The session ID is displayed in the welcome banner for easy reference.
|
||
# Logs contain full conversation history in trajectory format:
|
||
# - System prompt, user messages, assistant responses
|
||
# - Tool calls with inputs/outputs
|
||
# - Timestamps for debugging
|
||
#
|
||
# No configuration needed - logging is always enabled.
|
||
# To disable, you would need to modify the source code.
|
||
|
||
# =============================================================================
|
||
# Code Execution Sandbox (Programmatic Tool Calling)
|
||
# =============================================================================
|
||
# The execute_code tool runs Python scripts that call Hermes tools via RPC.
|
||
# Intermediate tool results stay out of the LLM's context window.
|
||
code_execution:
|
||
timeout: 300 # Max seconds per script before kill (default: 300 = 5 min)
|
||
max_tool_calls: 50 # Max RPC tool calls per execution (default: 50)
|
||
# Local execution uses a persistent session kernel: variables, imports, and
|
||
# loaded data survive across execute_code calls in one conversation, so
|
||
# multi-step data work stops re-loading its inputs every call. Pass
|
||
# reset=true on a call to discard state; a timed-out/interrupted cell kills
|
||
# the kernel (next call starts fresh). Remote terminal backends currently
|
||
# run per-call (remote kernel host is tracked follow-up work). Security
|
||
# scrubbing, tool whitelist, and output redaction are identical either way.
|
||
# kernel_idle_timeout: 1800 # Reap kernels idle longer than this (seconds)
|
||
# max_session_kernels: 4 # Process-wide LRU cap on live kernels
|
||
|
||
# =============================================================================
|
||
# Subagent Delegation
|
||
# =============================================================================
|
||
# The delegate_task tool spawns child agents with isolated context.
|
||
# Supports single tasks and batch mode (default 3 parallel, configurable).
|
||
delegation:
|
||
max_iterations: 250 # Max tool-calling turns per child (default: 250)
|
||
# max_concurrent_children: 10 # Max parallel child agents per batch (default: 10, floor: 1, no ceiling).
|
||
# WARNING: values above 10 multiply API cost linearly.
|
||
# max_spawn_depth: 1 # Delegation tree depth cap (range: 1-3, default: 1 = flat).
|
||
# Raise to 2 to allow workers to spawn their own subagents.
|
||
# Requires role="orchestrator" on intermediate agents.
|
||
# orchestrator_enabled: true # Kill switch for role="orchestrator" children (default: true).
|
||
# subagent_auto_approve: false # When a subagent hits a dangerous-command approval prompt, auto-deny (default: false)
|
||
# or auto-approve "once" (true) instead of blocking on stdin.
|
||
# The parent TUI owns stdin, so blocking would deadlock; non-interactive resolution is required.
|
||
# Both choices emit a logger.warning audit line. Flip to true only for cron/batch pipelines.
|
||
# inherit_mcp_toolsets: true # When explicit child toolsets are narrowed, also keep the parent's MCP toolsets (default: true). Set false for strict intersection.
|
||
# model: "google/gemini-3-flash-preview" # Override model for subagents (empty = inherit parent)
|
||
# provider: "openrouter" # Override provider for subagents (empty = inherit parent)
|
||
# # Resolves full credentials (base_url, api_key) automatically.
|
||
# # Supported: openrouter, nous, zai, kimi-coding, minimax
|
||
# # Cost tip: keep the parent on a frontier model and pin
|
||
# # delegation.model to an inexpensive one — children carry the
|
||
# # vast majority of tokens, so this is where spend is cut while
|
||
# # planning quality stays with the frontier parent.
|
||
|
||
# =============================================================================
|
||
# Honcho Integration (Cross-Session User Modeling)
|
||
# =============================================================================
|
||
# AI-native persistent memory via Honcho (https://honcho.dev/).
|
||
# Builds a deeper understanding of the user across sessions and tools.
|
||
# Runs alongside USER.md — additive, not a replacement.
|
||
#
|
||
# Requires: pip install honcho-ai
|
||
# Config: ~/.honcho/config.json (shared with Claude Code, Cursor, etc.)
|
||
# API key: HONCHO_API_KEY in ~/.hermes/.env or ~/.honcho/config.json
|
||
#
|
||
# Hermes-specific overrides (optional — most config comes from ~/.honcho/config.json):
|
||
# honcho: {}
|
||
|
||
# =============================================================================
|
||
# Display
|
||
# =============================================================================
|
||
display:
|
||
# Use compact banner mode (hides the ASCII-art banner, shows a single line).
|
||
# true: Compact single-line banner
|
||
# false: Full ASCII banner with tool/skill summary (default)
|
||
compact: false
|
||
|
||
# Tool progress display level (CLI and gateway)
|
||
# off: Silent — no tool activity shown, just the final response
|
||
# new: Show a tool indicator only when the tool changes (skip repeats)
|
||
# all: Show every tool call with a short preview (default)
|
||
# verbose: Full args, results, and debug logs (same as /verbose)
|
||
# log: Silent in chat; append every tool call to ~/.hermes/logs/tool_calls.log (gateway only)
|
||
# Toggle at runtime with /verbose in the CLI
|
||
tool_progress: all
|
||
|
||
# Per-platform defaults can be quieter than the global setting. Telegram
|
||
# tunes for mobile: tool_progress and busy_ack_detail default off (no
|
||
# per-tool breadcrumb stream, no "iteration 21/60" debug detail in busy
|
||
# acks or heartbeats), but interim_assistant_messages and
|
||
# long_running_notifications STAY ON so the user has real signal between
|
||
# turn start and final answer (mid-turn assistant commentary + a single
|
||
# edit-in-place "⏳ Working — N min" heartbeat). Override under
|
||
# display.platforms.telegram.
|
||
|
||
# Auto-cleanup of temporary progress bubbles after the final response lands.
|
||
# On platforms that support message deletion (currently Telegram), this
|
||
# removes the tool-progress bubble, "⏳ Still working..." notices, and
|
||
# context-pressure status messages once the final reply has been delivered —
|
||
# keeping long-running turns visible live, then tidy afterward. Failed runs
|
||
# leave the bubbles in place as breadcrumbs. Off by default.
|
||
# Per-platform override: display.platforms.telegram.cleanup_progress
|
||
# true: Delete tracked progress/status bubbles on successful turn
|
||
# false: Leave everything in place (default)
|
||
# Example:
|
||
# display:
|
||
# platforms:
|
||
# telegram:
|
||
# cleanup_progress: true
|
||
cleanup_progress: false
|
||
|
||
# Natural mid-turn assistant updates.
|
||
# On gateway platforms, when true, completed assistant status messages are
|
||
# sent as separate chat messages. On the Desktop app, when true, mid-turn
|
||
# assistant narration streamed between tool calls is kept in the transcript
|
||
# instead of the bubble collapsing to only the final message on completion.
|
||
# Independent of tool_progress and gateway streaming.
|
||
# true: Keep/send mid-turn assistant updates (default)
|
||
# false: Only keep/send the final response
|
||
interim_assistant_messages: true
|
||
|
||
# Gateway-only long-running status heartbeats.
|
||
# When false, the platform does not receive periodic "⏳ Working — N min"
|
||
# notifications even if agent.gateway_notify_interval is non-zero. The
|
||
# heartbeat edits a single message in place (where the adapter supports
|
||
# editing) instead of posting a new bubble each interval.
|
||
# Default: true everywhere, including Telegram (silent agents are worse
|
||
# than a single edit-in-place heartbeat).
|
||
long_running_notifications: true
|
||
|
||
# Include detailed iteration/tool/status context in busy acknowledgments
|
||
# and long-running heartbeats. When true, busy acks show "iteration 21/60,
|
||
# terminal, 10 min" and the heartbeat shows "⏳ Working — 12 min,
|
||
# iteration 21/60, terminal". When false (Telegram default), both stay
|
||
# terse: "Interrupting current task" and "⏳ Working — 12 min, terminal".
|
||
busy_ack_detail: true
|
||
|
||
# What Enter does when Hermes is already busy (CLI and gateway platforms).
|
||
# interrupt: Interrupt the current run and redirect Hermes (default)
|
||
# queue: Queue your message for the next turn
|
||
# steer: Inject your message mid-run via /steer, arriving at the agent
|
||
# after the next tool call — no interrupt, no role violation.
|
||
# Falls back to 'queue' if the agent isn't running yet or if
|
||
# images are attached (steer only carries text).
|
||
# Ctrl+C (or /stop in gateway) always interrupts regardless of this setting.
|
||
# Toggle at runtime with /busy <interrupt|queue|steer>.
|
||
busy_input_mode: interrupt
|
||
|
||
# Background process notifications (gateway/messaging only).
|
||
# Controls how chatty the process watcher is when you use
|
||
# terminal(background=true, notify_on_complete=true) from Telegram/Discord/etc.
|
||
# concise: One-line status message on completion; failures include a short
|
||
# output tail (default)
|
||
# off: No watcher messages at all
|
||
# result: Only the final raw-output completion message
|
||
# error: Only the final raw-output message when exit code != 0
|
||
# all: Running output updates + final raw-output message
|
||
background_process_notifications: concise
|
||
|
||
|
||
# Play terminal bell when agent finishes a response.
|
||
# Useful for long-running tasks — your terminal will ding when the agent is done.
|
||
# Works over SSH. Most terminals can be configured to flash the taskbar or play a sound.
|
||
# true: Ring the terminal bell on each response
|
||
# false: Silent (default)
|
||
bell_on_complete: false
|
||
|
||
# Play terminal bell when a blocking prompt opens and waits on you:
|
||
# clarify questions, dangerous-command approvals, sudo password, secret
|
||
# capture. Same mechanism as bell_on_complete (\a) — works over SSH.
|
||
# true: Ring whenever the agent is waiting for your input
|
||
# false: Silent (default)
|
||
bell_on_prompt: false
|
||
|
||
# Show model reasoning/thinking before each response.
|
||
# When enabled, a dim box shows the model's thought process above the response.
|
||
# Toggle at runtime with /reasoning show or /reasoning hide.
|
||
# true: Show the reasoning box
|
||
# false: Hide reasoning (default)
|
||
show_reasoning: false
|
||
|
||
# Stream tokens to the terminal as they arrive instead of waiting for the
|
||
# full response. The response box opens on first token and text appears
|
||
# line-by-line. Tool calls are still captured silently.
|
||
# true: Stream tokens as they arrive (default)
|
||
# false: Wait for the full response before rendering
|
||
streaming: true
|
||
|
||
# Show [HH:MM] timestamps on user input and assistant response labels.
|
||
# timestamps: false
|
||
|
||
# ───────────────────────────────────────────────────────────────────────────
|
||
# Skin / Theme
|
||
# ───────────────────────────────────────────────────────────────────────────
|
||
# Customize CLI visual appearance — banner colors, spinner faces, tool prefix,
|
||
# response box label, and branding text. Change at runtime with /skin <name>.
|
||
#
|
||
# Built-in skins:
|
||
# default — Classic Hermes gold/kawaii
|
||
# ares — Crimson/bronze war-god theme with spinner wings
|
||
# mono — Clean grayscale monochrome
|
||
# slate — Cool blue developer-focused
|
||
# daylight — Bright light-mode theme
|
||
# warm-lightmode — Warm paper-tone light-mode theme
|
||
# poseidon — Sea-green/teal Olympian theme
|
||
# sisyphus — Earthy stone-and-moss theme
|
||
# charizard — Fiery orange dragon theme
|
||
#
|
||
# Custom skins: drop a YAML file in ~/.hermes/skins/<name>.yaml
|
||
# Schema (all fields optional, missing values inherit from default):
|
||
#
|
||
# name: my-theme
|
||
# description: Short description
|
||
# colors:
|
||
# banner_border: "#HEX" # Panel border
|
||
# banner_title: "#HEX" # Panel title
|
||
# banner_accent: "#HEX" # Section headers (Available Tools, etc.)
|
||
# banner_dim: "#HEX" # Dim/muted text
|
||
# banner_text: "#HEX" # Body text (tool names, skill names)
|
||
# ui_accent: "#HEX" # UI accent color
|
||
# response_border: "#HEX" # Response box border color
|
||
# spinner:
|
||
# waiting_faces: ["(⚔)", "(⛨)"] # Faces shown while waiting
|
||
# thinking_faces: ["(⚔)", "(⌁)"] # Faces shown while thinking
|
||
# thinking_verbs: ["forging", "plotting"] # Verbs for spinner messages
|
||
# wings: # Optional left/right spinner decorations
|
||
# - ["⟪⚔", "⚔⟫"]
|
||
# - ["⟪▲", "▲⟫"]
|
||
# branding:
|
||
# agent_name: "My Agent" # Banner title and branding
|
||
# welcome: "Welcome message" # Shown at CLI startup
|
||
# response_label: " ⚔ Agent " # Response box header label
|
||
# prompt_symbol: "⚔" # Prompt symbol (bare token; renderers add trailing space)
|
||
# tool_prefix: "╎" # Tool output line prefix (default: ┊)
|
||
#
|
||
skin: default
|
||
|
||
# =============================================================================
|
||
# Model Aliases — short names for /model command
|
||
# =============================================================================
|
||
# Map short aliases to exact (model, provider, base_url) tuples.
|
||
# Used by /model tab completion and resolve_alias().
|
||
# Aliases are checked BEFORE the models.dev catalog, so they can route
|
||
# to endpoints not in the catalog (e.g. Ollama Cloud, local servers).
|
||
#
|
||
# An alias pointing at its own endpoint can carry that endpoint's
|
||
# credential with `api_key` (a literal, or a "${VAR}" reference) or
|
||
# `key_env` (an env var name). `api_key` wins if both are set. When
|
||
# neither is set the key is resolved from the alias HOST — never from
|
||
# whatever provider was active before the switch, which would send that
|
||
# provider's secret to an unrelated third party.
|
||
#
|
||
# model_aliases:
|
||
# opus:
|
||
# model: claude-opus-4-6
|
||
# provider: anthropic
|
||
# qwen:
|
||
# model: "qwen3.5:397b"
|
||
# provider: custom
|
||
# base_url: "https://ollama.com/v1"
|
||
# glm:
|
||
# model: glm-4.7
|
||
# provider: custom
|
||
# base_url: "https://ollama.com/v1"
|
||
# theta:
|
||
# model: theta-1
|
||
# provider: custom
|
||
# base_url: "https://theta.example.com/v1"
|
||
# key_env: THETA_API_KEY # or: api_key: "${THETA_API_KEY}"
|
||
|
||
# =============================================================================
|
||
# Privacy
|
||
# =============================================================================
|
||
# privacy:
|
||
# # Redact PII from the LLM context prompt.
|
||
# # When true, phone numbers are stripped and user/chat IDs are replaced
|
||
# # with deterministic hashes before being sent to the model.
|
||
# # Names and usernames are NOT affected (user-chosen, publicly visible).
|
||
# # Routing/delivery still uses the original values internally.
|
||
# redact_pii: false
|
||
|
||
# =============================================================================
|
||
# Shell-script hooks
|
||
# =============================================================================
|
||
# Register shell scripts as plugin-hook callbacks. Each entry is executed as
|
||
# a subprocess (shell=False, shlex.split) with a JSON payload on stdin. On
|
||
# stdout the script may return JSON that either blocks the tool call or
|
||
# injects context into the next LLM call.
|
||
#
|
||
# Valid events (mirror hermes_cli.plugins.VALID_HOOKS):
|
||
# pre_tool_call, post_tool_call, pre_llm_call, post_llm_call,
|
||
# pre_api_request, post_api_request, on_session_start, on_session_end,
|
||
# on_session_finalize, on_session_reset, subagent_stop
|
||
#
|
||
# First-use consent: each (event, command) pair prompts once on a TTY, then
|
||
# is persisted to ~/.hermes/shell-hooks-allowlist.json. Non-interactive
|
||
# runs (gateway, cron) need --accept-hooks, HERMES_ACCEPT_HOOKS=1, or the
|
||
# hooks_auto_accept key below.
|
||
#
|
||
# See website/docs/user-guide/features/hooks.md for the full JSON wire
|
||
# protocol and worked examples.
|
||
#
|
||
# hooks:
|
||
# pre_tool_call:
|
||
# - matcher: "terminal"
|
||
# command: "~/.hermes/agent-hooks/block-rm-rf.sh"
|
||
# timeout: 10
|
||
# post_tool_call:
|
||
# - matcher: "write_file|patch"
|
||
# command: "~/.hermes/agent-hooks/auto-format.sh"
|
||
# pre_llm_call:
|
||
# - command: "~/.hermes/agent-hooks/inject-cwd-context.sh"
|
||
# subagent_stop:
|
||
# - command: "~/.hermes/agent-hooks/log-orchestration.sh"
|
||
#
|
||
# hooks_auto_accept: false
|
||
|
||
|
||
# =============================================================================
|
||
# Telemetry
|
||
# =============================================================================
|
||
# Shared metrics are disabled by default. When enabled, Hermes writes only
|
||
# allowlisted aggregate counters and immutable JSON
|
||
# packages under $HERMES_HOME/telemetry/shared_metrics.
|
||
# Packages include a random profile-scoped ID that stays stable until this
|
||
# directory is deleted. It is not derived from hardware, account, or host data.
|
||
# Successfully exported local history is retained for 30 days; pending deltas
|
||
# are retained until they can be exported.
|
||
# This profile-owned choice is not overridden by managed-scope configuration.
|
||
#
|
||
# Nothing is uploaded unless you also set `send: true`. That is a separate
|
||
# opt-in and requires `enabled`; it never turns collection on by itself.
|
||
# When sending is on:
|
||
# * only packages whose entire collection period falls within one
|
||
# continuous recorded consent window are ever sent. Consent windows open
|
||
# when you enable sending and close when you disable it, so data
|
||
# collected before you opted in — or during any gap between opt-ins —
|
||
# stays on this machine;
|
||
# * each package carries the profile-scoped ID as-is. It is a random UUID
|
||
# with no hardware, account, or host-derived content, and deleting the
|
||
# shared-metrics directory resets it.
|
||
# See docs/observability/relay-shared-metrics.md (Appendix A) for the full
|
||
# consent, identity, retention, and deletion decisions.
|
||
telemetry:
|
||
shared_metrics:
|
||
enabled: false
|
||
send: false
|
||
# endpoint: https://telemetry.nousresearch.com/v1/telemetry
|
||
|
||
|
||
# =============================================================================
|
||
# Update Behavior
|
||
# =============================================================================
|
||
updates:
|
||
# Create a full HERMES_HOME zip before every `hermes update`.
|
||
# Backups land in ~/.hermes/backups/ and can be restored with `hermes import`.
|
||
# Off by default because large homes can add minutes to every update.
|
||
pre_update_backup: false
|
||
|
||
# Number of pre-update backup zips to retain.
|
||
backup_keep: 5
|
||
|
||
# What non-interactive updates do with local source edits in the Hermes repo.
|
||
# Interactive terminal updates always prompt before restoring the autostash.
|
||
#
|
||
# stash - auto-stash before pull, then auto-restore after success (default)
|
||
# discard - drop the update-created stash after success; use only on managed
|
||
# installs where local source edits should not persist
|
||
non_interactive_local_changes: "stash"
|
||
|
||
|
||
# =============================================================================
|
||
# Web Dashboard
|
||
# =============================================================================
|
||
# OAuth gate configuration for `hermes dashboard --host <non-loopback>`.
|
||
# The bundled Nous Portal plugin reads these on startup; settings here are
|
||
# the canonical surface. Each can be overridden by an environment variable:
|
||
#
|
||
# dashboard.oauth.client_id <- HERMES_DASHBOARD_OAUTH_CLIENT_ID
|
||
# dashboard.oauth.portal_url <- HERMES_DASHBOARD_PORTAL_URL
|
||
# dashboard.public_url <- HERMES_DASHBOARD_PUBLIC_URL
|
||
#
|
||
# Env wins when set to a non-empty value. This is what Fly.io's platform-
|
||
# secret injection uses to push per-deploy client_ids without needing to
|
||
# bake a config.yaml into the image. Empty env values are treated as unset
|
||
# so a provisioned-but-not-populated secret can't shadow a valid entry here.
|
||
#
|
||
# Local dev / on-prem deploys should typically set these via config.yaml
|
||
# (the ~/.hermes/.env file is reserved for API keys and secrets).
|
||
#
|
||
# dashboard:
|
||
# oauth:
|
||
# client_id: "" # agent:{instance_id}; Portal provisions this at deploy
|
||
# portal_url: "" # blank → default https://portal.nousresearch.com
|
||
#
|
||
# # Force the absolute base URL the OAuth callback (and any other public
|
||
# # URL the dashboard hands to external systems) is built from. Set this
|
||
# # for deploys behind reverse proxies that don't reliably forward
|
||
# # X-Forwarded-Host / X-Forwarded-Proto / X-Forwarded-Prefix (manual
|
||
# # nginx setups, on-prem ingresses, custom-domain Fly deploys without
|
||
# # full proxy header chains).
|
||
# #
|
||
# # When set, the value is the complete authority: scheme + host +
|
||
# # optional path prefix (e.g. "https://example.com/hermes"). The OAuth
|
||
# # callback URL becomes "<public_url>/auth/callback" — X-Forwarded-Prefix
|
||
# # is IGNORED on this code path because the operator has explicitly
|
||
# # declared the public URL and we no longer need to guess.
|
||
# #
|
||
# # Leave empty to use the existing proxy-header reconstruction (the
|
||
# # default — works on Fly.io out of the box).
|
||
# #
|
||
# # public_url: "https://example.com/hermes"
|
||
# #
|
||
# # Reverse proxies connecting from another container or host are not
|
||
# # trusted by default. Add only the proxy's exact IP address, or a bounded
|
||
# # CIDR for a dedicated proxy network, so X-Forwarded-Proto and
|
||
# # X-Forwarded-For can be honored. Loopback is always trusted. Wildcards
|
||
# # and /0 networks are rejected.
|
||
# #
|
||
# # trusted_proxies:
|
||
# # - "172.20.0.5"
|
||
# # # - "172.20.0.0/24" # dedicated network, if the IP is dynamic
|
||
#
|
||
# -----------------------------------------------------------------------------
|
||
# Self-hosted OIDC dashboard auth (generic OpenID Connect — Authentik,
|
||
# Keycloak, Zitadel, Authelia, Auth0, Okta, Google, …). Use this INSTEAD of the
|
||
# nous block above when gating the dashboard with your own identity provider.
|
||
# Each setting can be overridden by an environment variable:
|
||
#
|
||
# dashboard.oauth.self_hosted.issuer <- HERMES_DASHBOARD_OIDC_ISSUER
|
||
# dashboard.oauth.self_hosted.client_id <- HERMES_DASHBOARD_OIDC_CLIENT_ID
|
||
# dashboard.oauth.self_hosted.scopes <- HERMES_DASHBOARD_OIDC_SCOPES
|
||
# dashboard.oauth.self_hosted.client_secret <- HERMES_DASHBOARD_OIDC_CLIENT_SECRET
|
||
#
|
||
# dashboard:
|
||
# oauth:
|
||
# provider: self-hosted
|
||
# self_hosted:
|
||
# issuer: "https://auth.example.com/application/o/hermes/" # required
|
||
# client_id: "hermes-dashboard" # required
|
||
# scopes: "openid profile email" # optional
|
||
#
|
||
# # OPTIONAL — set ONLY if your IDP registered the client as
|
||
# # *confidential* (Authentik / Keycloak often default to this). When
|
||
# # set, Hermes authenticates the client at the token endpoint
|
||
# # (client_secret_basic or client_secret_post, auto-selected from the
|
||
# # IDP's discovery doc) IN ADDITION to PKCE. Leave unset for a public
|
||
# # (PKCE-only) client — the common case.
|
||
# #
|
||
# # This is a CREDENTIAL: prefer setting HERMES_DASHBOARD_OIDC_CLIENT_SECRET
|
||
# # in ~/.hermes/.env over putting it here in config.yaml.
|
||
# # client_secret: ""
|
||
|
||
# =============================================================================
|
||
# External secret sources
|
||
# =============================================================================
|
||
# Pull provider credentials from external secret managers at process startup
|
||
# instead of storing them in ~/.hermes/.env. Only the manager's bootstrap
|
||
# credential (e.g. BWS_ACCESS_TOKEN / OP_SERVICE_ACCOUNT_TOKEN) lives in .env
|
||
# (or your shell / desktop session); everything else rotates centrally.
|
||
# Failures never block startup — Hermes warns once and continues with
|
||
# whatever .env already had.
|
||
#
|
||
# Multiple sources can be enabled at once:
|
||
# - "mapped" sources (explicit VAR -> ref bindings, e.g. 1Password's env:
|
||
# map) beat "bulk" sources (whole-project dumps like Bitwarden BSM)
|
||
# - within a shape, the first source to claim a var wins; later claims
|
||
# are skipped with a startup warning (never a silent clobber)
|
||
# - a source's override_existing lets it beat .env/shell values, but
|
||
# never another secret source's claim
|
||
# Docs: https://hermes-agent.nousresearch.com/docs/user-guide/secrets/
|
||
#
|
||
# secrets:
|
||
# # Optional explicit ordering of enabled sources.
|
||
# # sources: [onepassword, bitwarden]
|
||
#
|
||
# # ---- Bitwarden Secrets Manager (bws CLI) --------------------------------
|
||
# bitwarden:
|
||
# enabled: false
|
||
# access_token_env: BWS_ACCESS_TOKEN # bootstrap token, sourced from .env
|
||
# project_id: "" # UUID of the BSM project to sync
|
||
# server_url: "" # "" = US Cloud; EU/self-hosted URL otherwise
|
||
# cache_ttl_seconds: 300 # 0 disables fresh caching
|
||
# encrypted_cache: # optional encrypted stale fallback
|
||
# enabled: false
|
||
# max_stale_seconds: 0 # 0 disables stale fallback
|
||
# override_existing: true # BSM values win over existing env
|
||
# auto_install: true # lazy-download bws into ~/.hermes/bin
|
||
#
|
||
# # ---- 1Password (op CLI) -------------------------------------------------
|
||
# onepassword:
|
||
# enabled: false
|
||
# # Map env-var names to op:// secret references. Each is resolved with a
|
||
# # single `op read` at startup.
|
||
# env:
|
||
# OPENAI_API_KEY: "op://Private/OpenAI/api key"
|
||
# ANTHROPIC_API_KEY: "op://Private/Anthropic/credential"
|
||
# account: "" # op --account shorthand; "" = default
|
||
# service_account_token_env: OP_SERVICE_ACCOUNT_TOKEN # headless auth; unset = desktop session
|
||
# binary_path: "" # "" = resolve op via PATH; else absolute path
|
||
# cache_ttl_seconds: 300 # 0 disables BOTH cache layers
|
||
# override_existing: true # resolved values win over existing env
|
||
#
|
||
# # ---- Command helper (any CLI vault) --------------------------------------
|
||
# # Run a user-configured helper that prints KEY=VALUE lines on stdout —
|
||
# # works with any secret store that has a CLI: keepassxc-cli, secret-tool,
|
||
# # pass, gpg, or a script that cats a tmpfs env file. Composes with the
|
||
# # sources above (enable any combination). POSIX-only (needs /bin/sh).
|
||
# # The helper must be fast and NON-interactive (hard timeout, 1 MiB cap);
|
||
# # its stderr is discarded so diagnostics can't leak secret material.
|
||
# command:
|
||
# enabled: false
|
||
# command: "cat /run/user/1000/hermes-secrets.env"
|
||
# helper_timeout_seconds: 3
|
||
# override_existing: false # .env/shell win by default
|
||
# # This runs ONCE per process at startup, so it cannot replace a credential
|
||
# # that expires mid-session. For a single provider whose token needs
|
||
# # re-minting during a session, use `providers.<name>.key_cmd` instead.
|